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. 3.
  2. 4.
  3. 5.
  4. 6.
  5. 7.
  6. 8.
  7. 9.
  8. 10.

    lib

  9. 11.
  10. 20.

    ... and so to Models class SomeModel < AR::Base include

    MineNotYours end m = SomeModel.find(:first) m.copyright # => "(c) me"
  11. 21.

    Adding methods to all models # Re-opening the target class

    # directly class ActiveRecord::Base include MineNotYours end
  12. 22.

    Adding methods to all models # Send the ‘include’ message

    to # our target class AR::Base.send(:include, MineNotYours)
  13. 23.
  14. 25.

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

    = ConferenceDelegate.new c.name = "Joe Blogs" c.name # => "Joe Blogs"
  15. 26.

    Plain Ol’ Ruby Objects class ConferenceDelegate :name end c =

    ConferenceDelegate.new c.name = "Joe Blogs" c.name # => "Joe Blogs" attr_accessor
  16. 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"]
  17. 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”
  18. 32.

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

    = RubyGuru.new g.first_name = “Dave” g.last_name = “Thomas”
  19. 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
  20. 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)
  21. 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
  22. 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
  23. 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
  24. 43.

    Changing existing behaviour # replacing implementation # via inheritance class

    Thing < Object def object_id @my_custom_value end end
  25. 48.

    Method chains with Rails class FunkyBass def play_it "Funky" end

    end bass = FunkyBass.new bass.play_it # => "Funky"
  26. 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:
  27. 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
  28. 53.
  29. 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
  30. 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)