SlideShare a Scribd company logo
1 of 55
Download to read offline
Plugins
James Adam
Rails
$ script/plugin install
lib
init.rb
tasks install.rb
uninstall.rb test
Plugins
developing
Rails plugins
can add
anything
Rails plugins
can change
anything
Rails plugins
can do
anything
Methods
Adding
with Ruby, and Rails
Modules
module MineNotYours
def copyright
"(c) me"
end
end
Adding methods to instances...
class SomeClass
include MineNotYours
end
c = SomeClass.new
c.copyright # => "(c) me"
... and so to Models
class SomeModel < AR::Base
include MineNotYours
end
m = SomeModel.find(:first)
m.copyright # => "(c) me"
Adding methods to all models
# Re-opening the target class
# directly
class ActiveRecord::Base
include MineNotYours
end
Adding methods to all models
# Send the ‘include’ message to
# our target class
AR::Base.send(:include, MineNotYours)
Behaviour
Adding
as a part of Class Definitions
Plain Ol’ Ruby Objects
class ConferenceDelegate
attr_accessor :name
end
c = ConferenceDelegate.new
c.name = "Joe Blogs"
c.name # => "Joe Blogs"
Plain Ol’ Ruby Objects
class ConferenceDelegate
:name
end
c = ConferenceDelegate.new
c.name = "Joe Blogs"
c.name # => "Joe Blogs"
attr_accessor
“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"]
Ruby Hacker; Know Thy-self
class ConferenceDelegate
attr_accessor :name
end
# self == ConferenceDelegate
Ruby Hacker; Know Thy-self
class ConferenceDelegate
def self.do_something
“OK”
end
do_something
end
# => “OK”
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”
Another module
module Personable
def has_person_attributes
attr_accessor :first_name
attr_accessor :last_name
end
end
Adding “class” methods
class RubyGuru
extend Personable
end
has_person_attributes
g = RubyGuru.new
g.first_name = “Dave”
g.last_name = “Thomas”
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
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)
Our own ActiveRecord behaviour
class Celebrity < AR::Base
acts_as_glamourous(50)
end
Class and instance behaviour
module GlamourPlugin
def acts_as_glamourous(size)
validates_presence_of :rich_boyfriend
# etc...
end
end
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
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
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
RailsPatching
Rails
RailsPatching
Rails
HACKING
changing the behaviour
of existing classes
Changing existing behaviour
# replacing implementation
# via inheritance
class Thing < Object
def object_id
@my_custom_value
end
end
Inheritance is not
always possible
ActiveRecord::Base
ActionController::Base
ActionView::Base
Dependencies ActionMailer::Base
Routing
DispatchingAssociations
... and more...
changing the
implementation of
existing methods
Ruby classes are always open
class ActiveRecord::Base
def count
execute("SELECT COUNT(*)
FROM #{table_name}") / 2
end
end
Aliasing methods
class ActiveRecord::Base
end
alias_method :__count, :count
def count
__count / 2
end
Method chains with Rails
class FunkyBass
def play_it
"Funky"
end
end
bass = FunkyBass.new
bass.play_it # => "Funky"
Method chains - new behaviour
module Soul
def play_it_with_soul
"Smooth & " +
play_it_without_soul
end
end
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:
Adding the new functionality
class FunkyBass
include Soul
alias_method_chain :play_it,
:soul
end
bass.play_it
# => "Smooth & Funky"
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
Patching Rails’ Dependencies
class Dependencies
def require_or_load(file_name)
# load the file from the normal
# places, i.e. $LOAD_PATH
end
end
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
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)

More Related Content

Similar to Extending Rails with Plugins (2007)

Ruby Metaprogramming
Ruby MetaprogrammingRuby Metaprogramming
Ruby Metaprogramming
Nando Vieira
 
Metaprogramovanie #1
Metaprogramovanie #1Metaprogramovanie #1
Metaprogramovanie #1
Jano Suchal
 
OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails Turtorial
Yi-Ting Cheng
 

Similar to Extending Rails with Plugins (2007) (20)

SOLID Ruby, SOLID Rails
SOLID Ruby, SOLID RailsSOLID Ruby, SOLID Rails
SOLID Ruby, SOLID Rails
 
Ruby Metaprogramming
Ruby MetaprogrammingRuby Metaprogramming
Ruby Metaprogramming
 
Say Goodbye to Procedural Programming - Nick Sutterer
Say Goodbye to Procedural Programming - Nick SuttererSay Goodbye to Procedural Programming - Nick Sutterer
Say Goodbye to Procedural Programming - Nick Sutterer
 
Model of the colossus @ Rupy Brazil 2013
Model of the colossus @ Rupy Brazil 2013 Model of the colossus @ Rupy Brazil 2013
Model of the colossus @ Rupy Brazil 2013
 
Working Effectively With Legacy Code
Working Effectively With Legacy CodeWorking Effectively With Legacy Code
Working Effectively With Legacy Code
 
Lightning talk
Lightning talkLightning talk
Lightning talk
 
Ruby: Beyond the Basics
Ruby: Beyond the BasicsRuby: Beyond the Basics
Ruby: Beyond the Basics
 
Metaprogramovanie #1
Metaprogramovanie #1Metaprogramovanie #1
Metaprogramovanie #1
 
Ruby on Rails at PROMPT ISEL '11
Ruby on Rails at PROMPT ISEL '11Ruby on Rails at PROMPT ISEL '11
Ruby on Rails at PROMPT ISEL '11
 
Dsl
DslDsl
Dsl
 
Intro to Ruby - Twin Cities Code Camp 7
Intro to Ruby - Twin Cities Code Camp 7Intro to Ruby - Twin Cities Code Camp 7
Intro to Ruby - Twin Cities Code Camp 7
 
Django Vs Rails
Django Vs RailsDjango Vs Rails
Django Vs Rails
 
Ruby on rails delegate
Ruby on rails delegateRuby on rails delegate
Ruby on rails delegate
 
OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails Turtorial
 
WordPress plugin #2
WordPress plugin #2WordPress plugin #2
WordPress plugin #2
 
The Ring programming language version 1.3 book - Part 30 of 88
The Ring programming language version 1.3 book - Part 30 of 88The Ring programming language version 1.3 book - Part 30 of 88
The Ring programming language version 1.3 book - Part 30 of 88
 
Module Magic
Module MagicModule Magic
Module Magic
 
Perl Teach-In (part 2)
Perl Teach-In (part 2)Perl Teach-In (part 2)
Perl Teach-In (part 2)
 
Template rendering in rails
Template rendering in rails Template rendering in rails
Template rendering in rails
 
PHP OOP Lecture - 02.pptx
PHP OOP Lecture - 02.pptxPHP OOP Lecture - 02.pptx
PHP OOP Lecture - 02.pptx
 

More from lazyatom

More from lazyatom (6)

Engines: Team Development on Rails (2005)
Engines: Team Development on Rails (2005)Engines: Team Development on Rails (2005)
Engines: Team Development on Rails (2005)
 
The Even Darker Art of Rails Engines (2009)
The Even Darker Art of Rails Engines (2009)The Even Darker Art of Rails Engines (2009)
The Even Darker Art of Rails Engines (2009)
 
Gem That (2009)
Gem That (2009)Gem That (2009)
Gem That (2009)
 
Do we need more test frameworks? (2011)
Do we need more test frameworks? (2011)Do we need more test frameworks? (2011)
Do we need more test frameworks? (2011)
 
IoT Printer (2012)
IoT Printer (2012)IoT Printer (2012)
IoT Printer (2012)
 
The Even Darker Art Of Rails Engines
The Even Darker Art Of Rails EnginesThe Even Darker Art Of Rails Engines
The Even Darker Art Of Rails Engines
 

Recently uploaded

AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
Alluxio, Inc.
 

Recently uploaded (20)

5 Reasons Driving Warehouse Management Systems Demand
5 Reasons Driving Warehouse Management Systems Demand5 Reasons Driving Warehouse Management Systems Demand
5 Reasons Driving Warehouse Management Systems Demand
 
Microsoft 365 Copilot; An AI tool changing the world of work _PDF.pdf
Microsoft 365 Copilot; An AI tool changing the world of work _PDF.pdfMicrosoft 365 Copilot; An AI tool changing the world of work _PDF.pdf
Microsoft 365 Copilot; An AI tool changing the world of work _PDF.pdf
 
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
 
Crafting the Perfect Measurement Sheet with PLM Integration
Crafting the Perfect Measurement Sheet with PLM IntegrationCrafting the Perfect Measurement Sheet with PLM Integration
Crafting the Perfect Measurement Sheet with PLM Integration
 
KLARNA - Language Models and Knowledge Graphs: A Systems Approach
KLARNA -  Language Models and Knowledge Graphs: A Systems ApproachKLARNA -  Language Models and Knowledge Graphs: A Systems Approach
KLARNA - Language Models and Knowledge Graphs: A Systems Approach
 
Top Mobile App Development Companies 2024
Top Mobile App Development Companies 2024Top Mobile App Development Companies 2024
Top Mobile App Development Companies 2024
 
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
 
Agnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in KrakówAgnieszka Andrzejewska - BIM School Course in Kraków
Agnieszka Andrzejewska - BIM School Course in Kraków
 
What need to be mastered as AI-Powered Java Developers
What need to be mastered as AI-Powered Java DevelopersWhat need to be mastered as AI-Powered Java Developers
What need to be mastered as AI-Powered Java Developers
 
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdfStrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
StrimziCon 2024 - Transition to Apache Kafka on Kubernetes with Strimzi.pdf
 
CompTIA Security+ (Study Notes) for cs.pdf
CompTIA Security+ (Study Notes) for cs.pdfCompTIA Security+ (Study Notes) for cs.pdf
CompTIA Security+ (Study Notes) for cs.pdf
 
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdf
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdfImplementing KPIs and Right Metrics for Agile Delivery Teams.pdf
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdf
 
INGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by DesignINGKA DIGITAL: Linked Metadata by Design
INGKA DIGITAL: Linked Metadata by Design
 
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAGAI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
AI/ML Infra Meetup | Reducing Prefill for LLM Serving in RAG
 
COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...
COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...
COMPUTER AND ITS COMPONENTS PPT.by naitik sharma Class 9th A mittal internati...
 
10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf10 Essential Software Testing Tools You Need to Know About.pdf
10 Essential Software Testing Tools You Need to Know About.pdf
 
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdfA Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
A Comprehensive Appium Guide for Hybrid App Automation Testing.pdf
 
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
A Python-based approach to data loading in TM1 - Using Airflow as an ETL for TM1
 
The Impact of PLM Software on Fashion Production
The Impact of PLM Software on Fashion ProductionThe Impact of PLM Software on Fashion Production
The Impact of PLM Software on Fashion Production
 
Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024
 

Extending Rails with Plugins (2007)

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