The document discusses various ways that Rails plugins can modify and extend the behavior of Rails and ActiveRecord classes and objects. It covers techniques like including modules to add methods, extending classes to add class methods, aliasing methods to modify existing behavior, and patching dependencies to change how Rails loads files. The goal of these techniques is to allow plugins to flexibly change and enhance the core Rails framework.
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
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...
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)