Pro Yearly is on sale from $80 to $50! »

Extending Rails with Plugins (2007)

Extending Rails with Plugins (2007)

(QCon London, 2007)

"One of the most important and useful aspects of the Rails framework is it's ability to be extended using plugins. Thanks to the flexibility and power of Ruby, almost any aspect of Rails can be altered to suit the needs of a particular application.

"This presentation will help give developers the boost that's often required to get up to speed developing plugins. We'll cover the hooks that Rails' plugin mechanism makes available, and how to put them to best use in practice with practical examples.

"Once we've covered the groundwork, we'll start to look at more advanced programming techniques for sharing code (and other files) between Rails applications. With a few key programming techniques under our belt, we can use plugins to alter and enhance the Rails framework itself."

Acd62030df551952268e84c8fff26a5b?s=128

lazyatom

March 16, 2007
Tweet

Transcript

  1. Plugins James Adam Rails

  2. $ script/plugin install

  3. None
  4. None
  5. None
  6. None
  7. None
  8. None
  9. None
  10. lib

  11. init.rb

  12. tasks install.rb uninstall.rb test

  13. Plugins developing

  14. Rails plugins can add anything

  15. Rails plugins can change anything

  16. Rails plugins can do anything

  17. Methods Adding with Ruby, and Rails

  18. Modules module MineNotYours def copyright "(c) me" end end

  19. Adding methods to instances... class SomeClass include MineNotYours end c

    = SomeClass.new c.copyright # => "(c) me"
  20. ... and so to Models class SomeModel < AR::Base include

    MineNotYours end m = SomeModel.find(:first) m.copyright # => "(c) me"
  21. Adding methods to all models # Re-opening the target class

    # directly class ActiveRecord::Base include MineNotYours end
  22. Adding methods to all models # Send the ‘include’ message

    to # our target class AR::Base.send(:include, MineNotYours)
  23. None
  24. Behaviour Adding as a part of Class Definitions

  25. Plain Ol’ Ruby Objects class ConferenceDelegate attr_accessor :name end c

    = ConferenceDelegate.new c.name = "Joe Blogs" c.name # => "Joe Blogs"
  26. Plain Ol’ Ruby Objects class ConferenceDelegate :name end c =

    ConferenceDelegate.new c.name = "Joe Blogs" c.name # => "Joe Blogs" attr_accessor
  27. “Class” methods irb:> c.class.private_methods => ["abort", "alias_method", "at_exit", "attr", "attr_accessor",

    "attr_reader", "attr_writer", "binding", "block_given?", "define_method", "eval", "exec", "exit", "extended", "fail", "fork", "getc", "gets", "global_variables", "include", "included", "irb_binding", "lambda", "load", "local_variables", "private", "proc", "protected", "public", "puts", "raise", "remove_class_variable", "remove_const", "remove_instance_variable", "remove_method", "require", "sleep", "split", "sprintf", "srand", "sub", "sub!", "syscall", "system", "test", "throw", "undef_method", "untrace_var", "warn"]
  28. Ruby Hacker; Know Thy-self class ConferenceDelegate attr_accessor :name end #

    self == ConferenceDelegate
  29. Ruby Hacker; Know Thy-self class ConferenceDelegate def self.do_something “OK” end

    do_something end # => “OK”
  30. Ruby Hacker; Know Thy-self class ConferenceDelegate def self.has_name attr_accessor :name

    end has_name end t = ConferenceDelegate.new t.name = “Joe Blogs”
  31. Another module module Personable def has_person_attributes attr_accessor :first_name attr_accessor :last_name

    end end
  32. Adding “class” methods class RubyGuru extend Personable end has_person_attributes g

    = RubyGuru.new g.first_name = “Dave” g.last_name = “Thomas”
  33. Specifying Behaviour in Rails class SuperModel < ActiveRecord::Base validates_presence_of :rich_boyfriend

    validates_size_of :entourage, :minimum => 20 has_many :vices, :through => :boyfriends end class FootballersWife < ActiveRecord::Base validates_presence_of :rich_boyfriend validates_size_of :entourage, :minimum => 5 has_many :vices, :through => :boyfriends end
  34. Bundling behaviour module ModelValidation def acts_as_glamourous(size) validates_presence_of :rich_boyfriend validates_size_of :entourage,

    :minimum => size has_many :vices, :through => :boyfriends end end # ... add this method to the target class ActiveRecord::Base.send(:extend, ModelValidation)
  35. Our own ActiveRecord behaviour class Celebrity < AR::Base acts_as_glamourous(50) end

  36. Class and instance behaviour module GlamourPlugin def acts_as_glamourous(size) validates_presence_of :rich_boyfriend

    # etc... end end
  37. Class and instance behaviour module GlamourPlugin def acts_as_glamourous validates_presence_of :rich_boyfriend

    # etc... end module InstanceBehaviour def react_to(other_person) if other_person.is_a?(Paparazzo) other_person.destroy end end end end
  38. Class and instance behaviour module GlamourPlugin def acts_as_glamourous validates_presence_of :rich_boyfriend

    # etc... include GlamourPlugin::InstanceBehaviour end module InstanceBehaviour def react_to(other_person) if other_person.is_a?(Paparazzo) other_person.destroy end end end end
  39. Our plugin in action class Diva < ActiveRecord::Base acts_as_glamourous end

    dude = Paparazzo.create(:lens => "Huge") Paparazzo.count # => 1 starlet = Diva.new(:name => "Britney", :entourage => 873, :quirk => "No hair") starlet.react_to(dude) Paparazzo.count # => 0
  40. Rails Patching Rails

  41. Rails Patching Rails HACKING

  42. changing the behaviour of existing classes

  43. Changing existing behaviour # replacing implementation # via inheritance class

    Thing < Object def object_id @my_custom_value end end
  44. Inheritance is not always possible ActiveRecord::Base ActionController::Base ActionView::Base Dependencies ActionMailer::Base

    Routing DispatchingAssociations ... and more...
  45. changing the implementation of existing methods

  46. Ruby classes are always open class ActiveRecord::Base def count execute("SELECT

    COUNT(*) FROM #{table_name}") / 2 end end
  47. Aliasing methods class ActiveRecord::Base end alias_method :__count, :count def count

    __count / 2 end
  48. Method chains with Rails class FunkyBass def play_it "Funky" end

    end bass = FunkyBass.new bass.play_it # => "Funky"
  49. Method chains - new behaviour module Soul def play_it_with_soul "Smooth

    & " + play_it_without_soul end end
  50. How alias_method_chain works class FunkyBass include Soul alias_method_chain :play_it, :soul

    end alias_method :play_it_without_soul, :play_it alias_method :play_it, :play_it_with_soul # underneath the hood:
  51. Adding the new functionality class FunkyBass include Soul alias_method_chain :play_it,

    :soul end bass.play_it # => "Smooth & Funky"
  52. Method chains in action class ActiveRecord::Base def count_with_fixes return count_without_fixes

    + 1 end alias_method_chain :count, :fixes end MyModel.count # calls new method
  53. Patching Rails’ Dependencies class Dependencies def require_or_load(file_name) # load the

    file from the normal # places, i.e. $LOAD_PATH end end
  54. New plugin-loading behaviour module LoadingFromPlugins # we want to replace

    Rails’ default loading # behaviour with this def require_or_load_with_plugins(file_name) if file_exists_in_plugin(file_name) load_file_from_plugin(file_name) else require_or_load_without_plugins(file_name) end end end
  55. Injecting the new behaviour module LoadingFromPlugins def require_or_load_with_plugins(file_name) if file_exists_in_plugins(file_name)

    load_file_from_plugin(file_name) else require_or_load_without_plugins(file_name) end end end def self.included(base) base.send(:alias_method_chain, :require_or_load, :plugins) end Dependencies.send(:include, LoadingFromPlugins)