Save 37% off PRO during our Black Friday Sale! »

Making Ruby Dance

2274a7476f6d2ac7aedcdec0651d0542?s=47 Ernie Miller
September 22, 2012

Making Ruby Dance

Lightning talk from Ruby Hoedown 2012 discussing dependency injection and assorted Ruby shenanigans.

Code: https://gist.github.com/3772859 https://gist.github.com/3772848
Video: http://www.youtube.com/watch?v=so8-u4F6K5s
Blog post: http://erniemiller.org/2012/09/23/making-ruby-dance/

2274a7476f6d2ac7aedcdec0651d0542?s=128

Ernie Miller

September 22, 2012
Tweet

Transcript

  1. Making Ruby Dance! Ernie Miller http://erniemiller.org @erniemiller Monday, September 24,

    12
  2. HAPPY CATURDAY, EVERYBODY! Monday, September 24, 12

  3. Everyone loves dependency injection! Monday, September 24, 12

  4. Everyone loves dependency injection! FACTS: Monday, September 24, 12

  5. Everyone loves dependency injection! • Decoupling your code makes it

    more reusable FACTS: Monday, September 24, 12
  6. Everyone loves dependency injection! • Decoupling your code makes it

    more reusable • Dependency injection is a great way to decouple your code FACTS: Monday, September 24, 12
  7. Everyone loves dependency injection! • Decoupling your code makes it

    more reusable • Dependency injection is a great way to decouple your code • Dependency injection will make you more attractive to the opposite sex FACTS: Monday, September 24, 12
  8. class Dancer attr_reader :name def initialize(name, dance_style) @name = name

    @dance_style = dance_style end def dance! @dance_style.dance!(self.name) end end Monday, September 24, 12
  9. def dance!(name) take_stage(name) cue_music perform_steps stop_music end Monday, September 24,

    12
  10. DEMO TIME! dancer = Dancer.new( 'PSY', GangnamStyle.new ) dancer.dance! Monday,

    September 24, 12
  11. BUT... Monday, September 24, 12

  12. BUT... • We need to... Monday, September 24, 12

  13. BUT... • We need to... • ... know the dependency

    we’re going to inject at instantiation time, or... Monday, September 24, 12
  14. BUT... • We need to... • ... know the dependency

    we’re going to inject at instantiation time, or... • ... add a mutator to our class. Monday, September 24, 12
  15. BUT... • We need to... • ... know the dependency

    we’re going to inject at instantiation time, or... • ... add a mutator to our class. • OR DO WE?!!?!? Monday, September 24, 12
  16. A brief digression Monday, September 24, 12

  17. A brief digression Monday, September 24, 12

  18. A brief digression Monday, September 24, 12

  19. A brief digression Monday, September 24, 12

  20. A brief digression Monday, September 24, 12

  21. A brief digression NON-COMPLIANT WITH TWITTER DISPLAY GUIDELINES Monday, September

    24, 12
  22. #my50lines Monday, September 24, 12

  23. INJECTION Monday, September 24, 12

  24. INJECTION.rb Monday, September 24, 12

  25. INJECTION.rb 50 lines, <= 80 columns Monday, September 24, 12

  26. INJECTION.rb 50 lines, <= 80 columns ...OF PURE EVIL. Monday,

    September 24, 12
  27. CODE! class Injection < Module attr_reader :name, :object, :as, :methods,

    :prefix, :suffix alias to_s name def initialize(object, *methods) opts = (methods.pop if Hash === methods.last) || {} @name, @object, @methods = "Injection(#{object})", object, methods @prefix, @suffix, @as = opts.values_at :prefix, :suffix, :as define_reader(as, object) define_delegators(object, methods) define_reflection end def extended(base) @object.injected(base) if @object.instance_eval { defined?(injected) } end def inspect @inspect ||= name.dup.tap do |name| name.insert(-2, " as: ##{as}") if as name.insert(-2, " methods: #{methods}") unless methods.empty? name.insert(-2, " format: #{affix('<method>')}") if prefix || suffix end end Monday, September 24, 12
  28. CODE! @inspect ||= name.dup.tap do |name| name.insert(-2, " as: ##{as}")

    if as name.insert(-2, " methods: #{methods}") unless methods.empty? name.insert(-2, " format: #{affix('<method>')}") if prefix || suffix end end private def affix(name) [prefix, name, suffix].compact.join('_') end def define_delegators(object, methods) methods.each do |name| define_method affix(name) do |*args, &block| object.__send__(name, *args, &block) end end end def define_reader(as, object) define_method(as) { object } if as end def define_reflection define_method :injections do singleton_class.ancestors.grep(Injection) end end end Monday, September 24, 12
  29. You’re a madman! Monday, September 24, 12

  30. You’re a madman! Yes. Monday, September 24, 12

  31. #!/usr/bin/env ruby require './injection' require 'pry' class CallTracker < BasicObject

    attr_reader :tracked_calls def initialize @tracked_calls = ::Hash.new { |h, k| h[k] = 0 } @injected = nil end def to_s "CallTracker" end def track(method) @tracked_calls[method] += 1 end def injected(base) [:public, :protected, :private].each do |visibility| methods = base.send "#{visibility}_methods" define_methods(base, visibility, methods) end end Monday, September 24, 12
  32. def injected(base) [:public, :protected, :private].each do |visibility| methods = base.send

    "#{visibility}_methods" define_methods(base, visibility, methods) end end def define_methods(base, visibility, methods) tracker = self base.singleton_class.class_eval do methods.each do |method| define_method method do |*args, &block| tracker.track(method) super(*args, &block) end send(visibility, method) end end end end array = [] tracker = CallTracker.new array.extend Injection.new(tracker, :tracked_calls) binding.pry Monday, September 24, 12
  33. Demo Time! Monday, September 24, 12