Classes, Modules, Objects and code • Well-defined interface to other components – gem/ruby dependencies (gemspec), class/object interfaces • As flexible as its components allow it to be gem install mobility
self.included(base) puts “included in #{base.name}” end end module MyModule def foo “foo” end def self.included(base) puts “included in #{base.name}” end end class A include MyModule def foo super + “bar” end end # prints “included in A” a = A.new a.foo #=> “foobar” class A include MyModule def foo super + “bar” end end # prints “included in A” a = A.new a.foo #=> “foobar”
config end end module MyModule(config) #=> SyntaxError: unexpected '\n'... def foo config end end Class A include MyModule(config) end #=> NoMethodError: undefined method `MyModule' for A:Class Class A include MyModule(config) end #=> NoMethodError: undefined method `MyModule' for A:Class
private def bar “bar” end end module MyModule def foo “foo” + bar end private def bar “bar” end end class A include MyModule def bar “baz” end end a = A.new a.foo #=> “foobaz” class A include MyModule def bar “baz” end end a = A.new a.foo #=> “foobaz”
of accessors x and y • Can we abstract our module so that defines addition on any set of accessors? • Can we do it in a way that supports method composition?
{ |key| send(key) + other.send(key) }) ) end end end module AdderDefiner def define_adder(*keys) define_method :+ do |other| self.class.new( *(keys.map { |key| send(key) + other.send(key) }) ) end end end Bootstrap Methods • Define a bootstrap method which builds instance methods dynamically from arguments
do |other| self.class.new( *(keys.map { |key| send(key) + other.send(key) }) ) end end include adder end end module AdderIncluder def define_adder(*keys) adder = Module.new do define_method :+ do |other| self.class.new( *(keys.map { |key| send(key) + other.send(key) }) ) end end include adder end end Module Boostrapper
do |other| self.class.new( *(keys.map { |key| send(key) + other.send(key) }) ) end end include adder end end module AdderIncluder def define_adder(*keys) adder = Module.new do define_method :+ do |other| self.class.new( *(keys.map { |key| send(key) + other.send(key) }) ) end end include adder end end Module Boostrapper AdderDefiner#define_adder
do |other| self.class.new( *(keys.map { |key| send(key) + other.send(key) }) ) end end include adder end end module AdderIncluder def define_adder(*keys) adder = Module.new do define_method :+ do |other| self.class.new( *(keys.map { |key| send(key) + other.send(key) }) ) end end include adder end end Module Boostrapper Anonymous module
do define_method :+ do |other| self.class.new(amount + other.amount, tax + other.tax) end end include adder def +(other) puts "Enter Adder..." super.tap { puts "Exit Adder..." } end end class LineItem < Struct.new(:amount, :tax) extend AdderIncluder adder = Module.new do define_method :+ do |other| self.class.new(amount + other.amount, tax + other.tax) end end include adder def +(other) puts "Enter Adder..." super.tap { puts "Exit Adder..." } end end Module Composition
do define_method :+ do |other| self.class.new(amount + other.amount, tax + other.tax) end end include adder def +(other) puts "Enter Adder..." super.tap { puts "Exit Adder..." } end end class LineItem < Struct.new(:amount, :tax) extend AdderIncluder adder = Module.new do define_method :+ do |other| self.class.new(amount + other.amount, tax + other.tax) end end include adder def +(other) puts "Enter Adder..." super.tap { puts "Exit Adder..." } end end Module Composition LineItem.ancestors #=> [LineItem, #<Module:0x...>, ...
y. AdderDefiner Adds class method define_adder which defines + method on configurable set of variables. AdderIncluder Also adds class method define_adder which defines + method, but in a composable way using anonymous module.
• Instance methods defined on anonymous module • Configuration state stored on including class, referenced by methods on anonymous module define_foo define_foo extend def foo(*args) self.class.foo end def foo(*args) self.class.foo end include MyClass @foo Module.new
initialize(*attribute_names) @names = attribute_names # ... # ... end def included(klass) @names.each do |name| define_method name do # ... end # ... end end end class Post include Attributes.new(:title, :content) end class Attributes < Module def initialize(*attribute_names) @names = attribute_names # ... # ... end def included(klass) @names.each do |name| define_method name do # ... end # ... end end end class Post include Attributes.new(:title, :content) end Subclass Module Define module methods in initialize or included hook Include instance of Module subclass in other classes Configure in initializer
@names = attribute_names # ... end # ... end class Post include Attributes.new(:title, :content) end class Attributes < Module def initialize(*attribute_names) @names = attribute_names # ... end # ... end class Post include Attributes.new(:title, :content) end
def included(klass) @names.each do |name| define_method name do locale = ... mobility_backend_for(name).read(locale) end end end end class Attributes < Module # ... def included(klass) @names.each do |name| define_method name do locale = ... mobility_backend_for(name).read(locale) end end end end
self.class.new( *(keys.map { |key| send(key) + other.send(key) }) ) end end end class AdderBuilder < Module def initialize(*keys) define_method :+ do |other| self.class.new( *(keys.map { |key| send(key) + other.send(key) }) ) end end end Addable as Module Builder
self.class.new( *(keys.map { |key| send(key) + other.send(key) }) ) end end end class AdderBuilder < Module def initialize(*keys) define_method :+ do |other| self.class.new( *(keys.map { |key| send(key) + other.send(key) }) ) end end end Addable as Module Builder AdderDefiner#define_adder
define_method :+ do |other| self.class.new( *(keys.map { |key| send(key) + other.send(key) }) ) end end def +(other) puts "Enter Adder..." super.tap { puts "Exit Adder..." } end end class LineItem < Struct.new(:amount, :tax) include (Module.new do define_method :+ do |other| self.class.new( *(keys.map { |key| send(key) + other.send(key) }) ) end end def +(other) puts "Enter Adder..." super.tap { puts "Exit Adder..." } end end
def +(other) puts "Enter Adder..." super.tap { puts "Exit Adder..." } end end class LineItem < Struct.new(:amount, :tax) include AdderBuilder.new(:amount, :tax) def +(other) puts "Enter Adder..." super.tap { puts "Exit Adder..." } end end LineItem.ancestors [LineItem, #<AdderBuilder:0x...>, ... ] It has a name!
@category = category end def build build_module do |category_module| define_inspect_method(category_module) define_unit_methods(category_module) end end private attr_reader :category def build_module(&block) Module.new do def self.define_unit_method(names) names.each do |name| define_method(name.to_sym) { Alchemist.measure self, name.to_sym } end end end.tap &block end def define_inspect_method(category_module) category_module.class_eval %(def self.inspect() "#<Module(#{category})>" end) end def define_unit_methods(category_module) category_module.class_eval category_methods end def library Alchemist.library end def category_methods unit_names.map do |name| %(define_method("#{name}") { Alchemist.measure self, :#{name} }) + "\n" + prefixed_methods(name) end.join("\n") end def unit_names library.unit_names(category) end def prefixes_for(name) if library.si_units.include?(name.to_s) library.unit_prefixes.keys else [] end end def prefixed_methods(name) prefixes_for(name).map do |prefix| %(define_method("#{prefix}#{name}") { Alchemist.measure_prefixed self, :#{prefix}, :#{name} }) end.join("\n") end end end module Alchemist class ModuleBuilder def initialize category @category = category end def build build_module do |category_module| define_inspect_method(category_module) define_unit_methods(category_module) end end private attr_reader :category def build_module(&block) Module.new do def self.define_unit_method(names) names.each do |name| define_method(name.to_sym) { Alchemist.measure self, name.to_sym } end end end.tap &block end def define_inspect_method(category_module) category_module.class_eval %(def self.inspect() "#<Module(#{category})>" end) end def define_unit_methods(category_module) category_module.class_eval category_methods end def library Alchemist.library end def category_methods unit_names.map do |name| %(define_method("#{name}") { Alchemist.measure self, :#{name} }) + "\n" + prefixed_methods(name) end.join("\n") end def unit_names library.unit_names(category) end def prefixes_for(name) if library.si_units.include?(name.to_s) library.unit_prefixes.keys else [] end end def prefixed_methods(name) prefixes_for(name).map do |prefix| %(define_method("#{prefix}#{name}") { Alchemist.measure_prefixed self, :#{prefix}, :#{name} }) end.join("\n") end end end BEFORE
@category = category end def build build_module do |category_module| define_inspect_method(category_module) define_unit_methods(category_module) end end private attr_reader :category def build_module(&block) Module.new do def self.define_unit_method(names) names.each do |name| define_method(name.to_sym) { Alchemist.measure self, name.to_sym } end end end.tap &block end def define_inspect_method(category_module) category_module.class_eval %(def self.inspect() "#<Module(#{category})>" end) end def define_unit_methods(category_module) category_module.class_eval category_methods end def library Alchemist.library end def category_methods unit_names.map do |name| %(define_method("#{name}") { Alchemist.measure self, :#{name} }) + "\n" + prefixed_methods(name) end.join("\n") end def unit_names library.unit_names(category) end def prefixes_for(name) if library.si_units.include?(name.to_s) library.unit_prefixes.keys else [] end end def prefixed_methods(name) prefixes_for(name).map do |prefix| %(define_method("#{prefix}#{name}") { Alchemist.measure_prefixed self, :#{prefix}, :#{name} }) end.join("\n") end end end module Alchemist class ModuleBuilder def initialize category @category = category end def build build_module do |category_module| define_inspect_method(category_module) define_unit_methods(category_module) end end private attr_reader :category def build_module(&block) Module.new do def self.define_unit_method(names) names.each do |name| define_method(name.to_sym) { Alchemist.measure self, name.to_sym } end end end.tap &block end def define_inspect_method(category_module) category_module.class_eval %(def self.inspect() "#<Module(#{category})>" end) end def define_unit_methods(category_module) category_module.class_eval category_methods end def library Alchemist.library end def category_methods unit_names.map do |name| %(define_method("#{name}") { Alchemist.measure self, :#{name} }) + "\n" + prefixed_methods(name) end.join("\n") end def unit_names library.unit_names(category) end def prefixes_for(name) if library.si_units.include?(name.to_s) library.unit_prefixes.keys else [] end end def prefixed_methods(name) prefixes_for(name).map do |prefix| %(define_method("#{prefix}#{name}") { Alchemist.measure_prefixed self, :#{prefix}, :#{name} }) end.join("\n") end end end module Alchemist class ModuleBuilder < Module def initialize category define_inspect_method(category) define_unit_methods(category) end def define_unit_method(names) names.each do |name| define_method(name.to_sym) { Alchemist.measure self, name.to_sym } end end private def define_inspect_method(category) define_method :inspect do "#<Module(#{category})>" end end def define_unit_methods(category) unit_names(category).map do |name| define_method name do Alchemist.measure self, name.to_sym end prefixes_for(name).map do |prefix| define_method "#{prefix}#{name}" do Alchemist.measure_prefixed self, prefix.to_sym, name.to_sym end end end end def library Alchemist.library end def unit_names(category) library.unit_names(category) end def prefixes_for(name) if library.si_units.include?(name.to_s) library.unit_prefixes.keys else [] end end end end module Alchemist class ModuleBuilder < Module def initialize category define_inspect_method(category) define_unit_methods(category) end def define_unit_method(names) names.each do |name| define_method(name.to_sym) { Alchemist.measure self, name.to_sym } end end private def define_inspect_method(category) define_method :inspect do "#<Module(#{category})>" end end def define_unit_methods(category) unit_names(category).map do |name| define_method name do Alchemist.measure self, name.to_sym end prefixes_for(name).map do |prefix| define_method "#{prefix}#{name}" do Alchemist.measure_prefixed self, prefix.to_sym, name.to_sym end end end end def library Alchemist.library end def unit_names(category) library.unit_names(category) end def prefixes_for(name) if library.si_units.include?(name.to_s) library.unit_prefixes.keys else [] end end end end BEFORE AFTER
@category = category end def build build_module do |category_module| define_inspect_method(category_module) define_unit_methods(category_module) end end private attr_reader :category def build_module(&block) Module.new do def self.define_unit_method(names) names.each do |name| define_method(name.to_sym) { Alchemist.measure self, name.to_sym } end end end.tap &block end def define_inspect_method(category_module) category_module.class_eval %(def self.inspect() "#<Module(#{category})>" end) end def define_unit_methods(category_module) category_module.class_eval category_methods end def library Alchemist.library end def category_methods unit_names.map do |name| %(define_method("#{name}") { Alchemist.measure self, :#{name} }) + "\n" + prefixed_methods(name) end.join("\n") end def unit_names library.unit_names(category) end def prefixes_for(name) if library.si_units.include?(name.to_s) library.unit_prefixes.keys else [] end end def prefixed_methods(name) prefixes_for(name).map do |prefix| %(define_method("#{prefix}#{name}") { Alchemist.measure_prefixed self, :#{prefix}, :#{name} }) end.join("\n") end end end module Alchemist class ModuleBuilder def initialize category @category = category end def build build_module do |category_module| define_inspect_method(category_module) define_unit_methods(category_module) end end private attr_reader :category def build_module(&block) Module.new do def self.define_unit_method(names) names.each do |name| define_method(name.to_sym) { Alchemist.measure self, name.to_sym } end end end.tap &block end def define_inspect_method(category_module) category_module.class_eval %(def self.inspect() "#<Module(#{category})>" end) end def define_unit_methods(category_module) category_module.class_eval category_methods end def library Alchemist.library end def category_methods unit_names.map do |name| %(define_method("#{name}") { Alchemist.measure self, :#{name} }) + "\n" + prefixed_methods(name) end.join("\n") end def unit_names library.unit_names(category) end def prefixes_for(name) if library.si_units.include?(name.to_s) library.unit_prefixes.keys else [] end end def prefixed_methods(name) prefixes_for(name).map do |prefix| %(define_method("#{prefix}#{name}") { Alchemist.measure_prefixed self, :#{prefix}, :#{name} }) end.join("\n") end end end module Alchemist class ModuleBuilder < Module def initialize category define_inspect_method(category) define_unit_methods(category) end def define_unit_method(names) names.each do |name| define_method(name.to_sym) { Alchemist.measure self, name.to_sym } end end private def define_inspect_method(category) define_method :inspect do "#<Module(#{category})>" end end def define_unit_methods(category) unit_names(category).map do |name| define_method name do Alchemist.measure self, name.to_sym end prefixes_for(name).map do |prefix| define_method "#{prefix}#{name}" do Alchemist.measure_prefixed self, prefix.to_sym, name.to_sym end end end end def library Alchemist.library end def unit_names(category) library.unit_names(category) end def prefixes_for(name) if library.si_units.include?(name.to_s) library.unit_prefixes.keys else [] end end end end module Alchemist class ModuleBuilder < Module def initialize category define_inspect_method(category) define_unit_methods(category) end def define_unit_method(names) names.each do |name| define_method(name.to_sym) { Alchemist.measure self, name.to_sym } end end private def define_inspect_method(category) define_method :inspect do "#<Module(#{category})>" end end def define_unit_methods(category) unit_names(category).map do |name| define_method name do Alchemist.measure self, name.to_sym end prefixes_for(name).map do |prefix| define_method "#{prefix}#{name}" do Alchemist.measure_prefixed self, prefix.to_sym, name.to_sym end end end end def library Alchemist.library end def unit_names(category) library.unit_names(category) end def prefixes_for(name) if library.si_units.include?(name.to_s) library.unit_prefixes.keys else [] end end end end BEFORE AFTER
method define_method(method) do |*args, &block| value = nil self.class.trace_execution_scoped([@method]) do value = super(*args, &block) end value end end def prepended(klass) klass.extend ::NewRelic::Agent::MethodTracer unless klass.is_a?(::NewRelic::Agent::MethodTracer) end end Sleeper.prepend Instrumentation.new(:sleep) Sleeper.prepend Instrumentation.new(:deep_sleep) class Intrumentation < Module def initialize(method) @method = method define_method(method) do |*args, &block| value = nil self.class.trace_execution_scoped([@method]) do value = super(*args, &block) end value end end def prepended(klass) klass.extend ::NewRelic::Agent::MethodTracer unless klass.is_a?(::NewRelic::Agent::MethodTracer) end end Sleeper.prepend Instrumentation.new(:sleep) Sleeper.prepend Instrumentation.new(:deep_sleep)