Slide 1

Slide 1 text

Plugins James Adam Rails

Slide 2

Slide 2 text

$ script/plugin install

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

lib

Slide 11

Slide 11 text

init.rb

Slide 12

Slide 12 text

tasks install.rb uninstall.rb test

Slide 13

Slide 13 text

Plugins developing

Slide 14

Slide 14 text

Rails plugins can add anything

Slide 15

Slide 15 text

Rails plugins can change anything

Slide 16

Slide 16 text

Rails plugins can do anything

Slide 17

Slide 17 text

Methods Adding with Ruby, and Rails

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

Adding methods to instances... class SomeClass include MineNotYours end c = SomeClass.new c.copyright # => "(c) me"

Slide 20

Slide 20 text

... and so to Models class SomeModel < AR::Base include MineNotYours end m = SomeModel.find(:first) m.copyright # => "(c) me"

Slide 21

Slide 21 text

Adding methods to all models # Re-opening the target class # directly class ActiveRecord::Base include MineNotYours end

Slide 22

Slide 22 text

Adding methods to all models # Send the ‘include’ message to # our target class AR::Base.send(:include, MineNotYours)

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

Behaviour Adding as a part of Class Definitions

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

“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"]

Slide 28

Slide 28 text

Ruby Hacker; Know Thy-self class ConferenceDelegate attr_accessor :name end # self == ConferenceDelegate

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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”

Slide 31

Slide 31 text

Another module module Personable def has_person_attributes attr_accessor :first_name attr_accessor :last_name end end

Slide 32

Slide 32 text

Adding “class” methods class RubyGuru extend Personable end has_person_attributes g = RubyGuru.new g.first_name = “Dave” g.last_name = “Thomas”

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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)

Slide 35

Slide 35 text

Our own ActiveRecord behaviour class Celebrity < AR::Base acts_as_glamourous(50) end

Slide 36

Slide 36 text

Class and instance behaviour module GlamourPlugin def acts_as_glamourous(size) validates_presence_of :rich_boyfriend # etc... end end

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

Rails Patching Rails

Slide 41

Slide 41 text

Rails Patching Rails HACKING

Slide 42

Slide 42 text

changing the behaviour of existing classes

Slide 43

Slide 43 text

Changing existing behaviour # replacing implementation # via inheritance class Thing < Object def object_id @my_custom_value end end

Slide 44

Slide 44 text

Inheritance is not always possible ActiveRecord::Base ActionController::Base ActionView::Base Dependencies ActionMailer::Base Routing DispatchingAssociations ... and more...

Slide 45

Slide 45 text

changing the implementation of existing methods

Slide 46

Slide 46 text

Ruby classes are always open class ActiveRecord::Base def count execute("SELECT COUNT(*) FROM #{table_name}") / 2 end end

Slide 47

Slide 47 text

Aliasing methods class ActiveRecord::Base end alias_method :__count, :count def count __count / 2 end

Slide 48

Slide 48 text

Method chains with Rails class FunkyBass def play_it "Funky" end end bass = FunkyBass.new bass.play_it # => "Funky"

Slide 49

Slide 49 text

Method chains - new behaviour module Soul def play_it_with_soul "Smooth & " + play_it_without_soul end end

Slide 50

Slide 50 text

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:

Slide 51

Slide 51 text

Adding the new functionality class FunkyBass include Soul alias_method_chain :play_it, :soul end bass.play_it # => "Smooth & Funky"

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

Patching Rails’ Dependencies class Dependencies def require_or_load(file_name) # load the file from the normal # places, i.e. $LOAD_PATH end end

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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)