Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Meta Programming in Ruby

Meta Programming in Ruby

Suraj Shirvankar

May 03, 2015
Tweet

More Decks by Suraj Shirvankar

Other Decks in Technology

Transcript

  1. Ruby Objects • Every thing in ruby is an object

    except for blocks; • Methods are called by passing messages to objects
  2. Monkey patching Allows you to refine a class / Add

    new methods Not recommended for Classes that you didn't build yourself class String def censor “######” end end “VPN”.sensor => “#####”
  3. MethodNotFound class Car ….. ….. end @car = Car.new @car.autodrive

    NoMethodError: undefined method `autodrive' for Car:Class
  4. What happens in the background class Car ….. ….. end

    The message ‘autodrive’ is sent to the car instance Ruby looks for the method ‘autodrive' and if it doesn't find the ‘autodrive’ method it calls ‘method_missing’ method_missing then throws the error NoMethodError
  5. Naive way of implementing dynamic methods def method_missing(method_name, *args, &block)

    if method_name == “autodrive” “Awaiting google’s implementation” else super end end @car.autodrive => “Awaiting google’s implementation”
  6. But Wait ! @car.autodrive => “Awaiting google’s implementation” @car.respond_to? :autodrive

    => false class Car def respond_to_missing?(method_name, include_private = false) method_name == “autodrive” || super end end
  7. class Car define_method(:break) do “Need to repair” end end @car.break

    => “Need to repair” @car.respond_to?(:break) => true
  8. Tests first require 'test/unit' require_relative '../lib/factory_boy' class TestFactoryBoy < Test::Unit::TestCase

    def setup FactoryBoy.define do factory :user do name "test" cat "meow" end end @user = FactoryBoy.build :user end def test_creates_user assert_equal @user.class, User end def test_has_name assert_equal @user.name, "test" end def test_meow assert_equal @user.cat, "meow" end end
  9. Implementation module FactoryBoy @factories = {} def self.define(&block) module_eval(&block) end

    def self.build(klass) instance = const_get(@factories[klass].class_name.capitalize).new @factories[klass].our_methods.each do |key, value| instance.class.class_eval do define_method(key.to_sym) { value } end end instance end def self.factory(klass, &block) instance = @factories[klass] = BaseObject.new(klass.to_s.capitalize) instance.instance_eval(&block) end class BaseObject < BasicObject attr_accessor :our_methods, :class_name def initialize(klass) @our_methods = {} @class_name = klass.to_s end def method_missing(method, *args, &block) @our_methods[method] = args.first end end end