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

meta

John DeWyze
November 11, 2016

 meta

My Meta Moments at RubyConf 2016

John DeWyze

November 11, 2016
Tweet

Other Decks in Technology

Transcript

  1. MY META MOMENTS ABOUT ME ▸Software Developer at Braintree in

    Chicago ▸Working in the field for a little over 2 years ▸ @dewyze ▸speakerdeck.com/dewyze/meta
  2. MY META MOMENTS REQUIRED DISCLAIMER ▸The views expressed in this

    presentation are my own, and not those of PayPal or any of its affiliates
  3. MY META MOMENTS WHAT IS METAPROGRAMMING? ▸ Writing code that

    writes code ▸ FactoryGirl.create(:user) ▸ Writing code with incomplete information ▸ User.find_by_username("matz") ▸ Writing code really efficiently ▸ user.methods.grep(/^validate_/).each do |method|
 user.send(method)
 end
  4. MY META MOMENTS PERILS OF METAPROGRAMMING ▸ Metaprogramming can produce

    lots of unexpected behavior. ▸ user.methods.grep(/^validate_/).each do |method|
 user.send(method)
 end ▸ Metaprogramming can be hard to test ▸ Metaprogramming is slow ▸ Metaprogramming is meta
  5. MY META MOMENTS FINAL CAVEATS ▸ Metaprogramming is like spider-man

    with a light saber ▸ With great power comes great responsibility ▸ Light sabers are super fun ▸ They can also very easily dismember you ▸ If you have one, you're probably a jedi
  6. MY META MOMENTS INTRODUCING: PUTTER ▸ "puts debugging" gem ▸

    puts "GOT HERE" ▸ puts "args 2: #{args[2]}" ▸ github.com/dewyze/putter ▸ It rhymes with gooder not gutter
  7. # watch.rb require 'putter' class User attr_accessor :username def initialize(num)

    @username = "user_#{num}" end def uppercase; @username.upcase!; end def lowercase; @username.downcase!; end end
  8. Putter.watch(User) users = [] (0..4).each do |num| user = User.new(num)

    users << user user.uppercase end users.each_with_index do |user, i| user.lowercase if i % 2 == 0 end
  9. → ruby watch.rb Putter Debugging: User .watch.rb:15:in `block in <main>'

    -- Method: :new, Args: [0], Result: #<User:0x007f8fa2948610> Putter Debugging: User instance 1 .watch.rb:17:in `block in <main>' -- Method: :uppercase, Args: [], Result: USER_0 Putter Debugging: User .watch.rb:15:in `block in <main>' -- Method: :new, Args: [1], Result: #<User:0x007f8fa29420f8> Putter Debugging: User instance 2 .watch.rb:17:in `block in <main>' -- Method: :uppercase, Args: [], Result: USER_1 Putter Debugging: User .watch.rb:15:in `block in <main>' -- Method: :new, Args: [2], Result: #<User:0x007f8fa293bf28> Putter Debugging: User instance 3 .watch.rb:17:in `block in <main>' -- Method: :uppercase, Args: [], Result: USER_2 Putter Debugging: User .watch.rb:15:in `block in <main>' -- Method: :new, Args: [3], Result: #<User:0x007f8fa2939138> Putter Debugging: User instance 4 .watch.rb:17:in `block in <main>' -- Method: :uppercase, Args: [], Result: USER_3 Putter Debugging: User .watch.rb:15:in `block in <main>' -- Method: :new, Args: [4], Result: #<User:0x007f8fa2933210> Putter Debugging: User instance 5 .watch.rb:17:in `block in <main>' -- Method: :uppercase, Args: [], Result: USER_4 Putter Debugging: User instance 1 .watch.rb:20:in `block in <main>' -- Method: :lowercase, Args: [], Result: user_0 Putter Debugging: User instance 3 .watch.rb:20:in `block in <main>' -- Method: :lowercase, Args: [], Result: user_2 Putter Debugging: User instance 5 .watch.rb:20:in `block in <main>' -- Method: :lowercase, Args: [], Result: user_4
  10. Putter Debugging: User .watch.rb:15:in `block in <main>' -- Method: :new,

    Args: [0], Result: #<User:0x007f8fa2948610> Putter Debugging: User instance 1 .watch.rb:17:in `block in <main>' -- Method: :uppercase, Args: [], Result: USER_0 Putter Debugging: User instance 5 .watch.rb:20:in `block in <main>' -- Method: :lowercase, Args: [], Result: user_4
  11. MY META MOMENTS TOPICS ▸ send ▸ Inheritance ▸ ancestors

    ▸ prepend ▸ Method dispatch ▸ method_missing(method, *args, &blk) ▸ define_method(method, &blk) ▸ BasicObject ▸ singleton_class ▸ instance_eval, class_eval, s/eval/exec
  12. $ class Sender $ def send_me $ "I WAS SENT!"

    $ end $ end $ Sender.new.send(:send_me) => "I WAS SENT!"
  13. MY META MOMENTS INHERITANCE ▸ class Truck < Vehicle ▸

    class User < ActiveRecord::Base ▸ "is a?" ▸ A truck or car can ".drive" or have "make" and "model" ▸ User.new.save! or User.new.update_attributes
  14. MY META MOMENTS ▸include and extend ▸class Television
 extend Broadcastable


    include Watchable ▸This changes the inheritance chain INHERITANCE
  15. $ module Parent $ def parent_method; "IM PARENTING";end $ end

    $ class Child $ include Parent $ def parent_method; super; end $ end $ Child.new.parent_method => "IM PARENTING"
  16. MY META MOMENTS ANCESTORS ▸:ancestors shows inheritance chain and order

    ▸Almost all object inherit from Object ▸mixes in Kernel (which is where puts comes from) ▸All objects inherit from BasicObject ▸Last module included is the first parent
  17. $ module Grandparent; end $ module Parent; end $ class

    Child $ include Grandparent $ include Parent $ end => Child
  18. $ module Proxy $ def proxy; puts "PROXIED"; super; end

    $ end $ class Test; prepend Proxy $ def proxy; "GOT HERE"; end $ end $ Test.new.proxy PROXIED => "GOT HERE"
  19. $ module NavientLoan; end $ module SallieMaeLoan $ include NavientLoan

    $ end $ module USGovLoan $ def owner $ "ALL YOUR LOAN ARE BELONG TO US" $ end $ end $ class Loan $ include USGovLoan $ include SallieMaeLoan $ end
  20. $ loan = Loan.new => #<Loan:0x007fb1a4860198> $ loan.owner => "ALL

    YOUR LOAN ARE BELONG TO US" $ module NavientLoan $ def owner $ "YOUR LOANS HAVE BEEN SOLD" $ end $ end $ loan.owner => "YOUR LOANS HAVE BEEN SOLD"
  21. MY META MOMENTS WHAT DOES THIS HAVE TO DO WITH

    METAPROGRAMMING? ▸None of this so far has really been metaprogramming ▸Knowledge is power! ▸Now on to the cool stuff
  22. MY META MOMENTS method_missing(method, *args, &blk) ▸Called when a method

    cannot be found ▸First "method missing" in ancestors will be used ▸Has access to the method, args, and any blocks passed to it
  23. $ module Parent $ def method_missing(method, *args) $ puts "METHOD:

    #{method}" $ puts "ARGS: #{args}" $ end $ end $ class Child $ include Parent $ end $ Child.new.do_stuff("AND", "THINGS") METHOD: do_stuff ARGS: ["AND", "THINGS"]
  24. $ class LazyRecord $ def method_missing(m, *args) $ if m

    =~ /find_by_/ $ trait = m.to_s.split("find_by_")[1] $ self.where("#{trait} = ?", args[0]) $ elsif m =~ /where/ $ puts "DATABASE LOOKUP" $ "MICKEY MOUSE" $ end; end; end
  25. $ class User < LazyRecord; end $ User.find_by_name("MICKEY") "DATABASE LOOKUP"

    select * from users where name ='MICKEY'; => "MICKEY MOUSE"
  26. MY META MOMENTS define_method(method, &blk) ▸Define a method via another

    method (without def) ▸Create methods whenever you want ▸like on method_missing ▸Takes a method name and a block
  27. $ class Define $ def method_missing(method, &blk) $ if blk

    $ puts "DEFINING CUSTOM METHOD" $ self.class.send( $ :define_method, method, &blk) $ else $ puts "DEFINING HELLO METHOD" $ self.class.send( $ :define_method, method) do $ "HELLO #{method}" $ end; end; end; end
  28. $ Define.new.world DEFINING HELLO METHOD $ Define.new.world => "HELLO world"

    $ Define.new.revolt do |arg| $ "I DONT WANT TO SAY HELLO #{arg}" $ end DEFINING CUSTOM METHOD $ Define.new.revolt("WORLD") => "I DONT WANT TO SAY HELLO WORLD"
  29. MY META MOMENTS BasicObject ▸ BasicObject is the source of

    all objects ▸ Handful of instance methods ▸ Very useful for metaprogramming BasicObject.instance_methods => [:!, :==, :!=, :__send__, :equal?, :instance_eval, :instance_exec, :__id__, :__binding__]
  30. MY META MOMENTS ▸ It also is the source of

    the magical method_missing as well as the necessary initialize BasicObject.private_instance_methods => [:method_missing, :singleton_method_added, :singleton_method_removed, :singleton_method_undefined, :initialize] BasicObject
  31. MY META MOMENTS SINGLETON CLASSES (OR METACLASS OR EIGENCLASS) ▸

    The class itself as an object ▸ Not an instance like User.new ▸ Class methods are just instance methods on the singleton class ▸ class User; def self.all { <Gets all the users> }; end ▸ User.all ▸ When we say a class method, we are referring to a method on the singleton class
  32. $ class Greeter $ def self.hello; "hello"; end $ def

    Greeter.hi; "hi"; end $ class << self $ def yo; "yo"; end $ end $ end $ Greeter.hello => "hello" $ Greeter.hi => "hi" $ Greeter.yo => "yo" $ Greeter.singleton_methods => [:hello, :hi, :yo]
  33. $ Greeter.methods - Object.methods => [:hello, :hi, :yo] $ Greeter.

    $ singleton_class. $ instance_methods - Object.methods => [:hello, :hi, :yo]
  34. MY META MOMENTS SINGLETON CLASSES ON INSTANCES ▸Instances can have

    their own singleton_class ▸Modifications only affect instance
  35. $:> class Thing;end $:> Thing.singleton_class => #<Class:Thing> $:> thing =

    Thing.new => #<Thing:0x007f86ac971a28> $:> thing.singleton_class => #<Class:#<Thing:0x007f86ac971a28>> $:> thing.singleton_class.ancestors => [#<Class:#<Thing:0x007f86ac971a28>>, Thing, Object, PP::ObjectMixin, Kernel, BasicObject]
  36. MY META MOMENTS instance_eval/class_eval(*args, &blk) (and exec) ▸Eval code in

    instances or classes ▸Eval does not take a method, exec does
  37. $ class Spy $ def initialize $ @secrets = "APPLE

    PIE RECIPE" $ end $ end $ s = Spy.new $ s.secrets NoMethodError: undefined method `secrets' $ s.instance_variable_get("@secrets") => "APPLE PIE RECIPE"
  38. $ class Neo; end $ keanu = Neo.new => #<Neo:0x007ffcc92a8b40>

    $ Neo.class_exec(:train) do |method| $ define_method(method) do $ "I KNOW KUNG FU" $ end $ end $ keanu.train => "I KNOW KUNG FU"
  39. follower = Putter.follow(object) PUTTER::FOLLOWER @object = USER OBJECT NEW PROXY

    @object .singleton_class .send(:prepend, proxy) MY META MOMENTS PUTTER::FOLLOWER USER 
 OBJECT PROXY
  40. MY META MOMENTS PROXY.instance_exec(data) do define_method(data.method, *args, &blk) do result

    = super *args, &blk puts "LOG INFO…" result end end else USER OBJECT #do_something("stuff") PROXY#do_something("stuff") PUTTER::FOLLOWER#add_method?(:do_something)
  41. Putter.watch(User) MY META MOMENTS class << User prepend InstanceFollower prepend

    Watcher.build_proxy(self) end INSTANCE FOLLOWER def new(…) obj = super @objs << obj Putter.follow(obj) end PUTTER::WATCHER .build_proxy( User .singleton_class )
  42. MY META MOMENTS USER.proxy_methods.each do |m| PROXY.instance_exec(data) do define_method(m, *args,

    &blk) do result = super *args, &blk puts "LOG INFO…" result end end end PUTTER::WATCHER#build_proxy?(User) USER CLASS .proxy_methods PROXY.proxy_methods INSTANCE FOLLOWER.new
  43. MY META MOMENTS User.do_something("stuff") User.new PUTTER::FOLLOWER USER 
 OBJECT PROXY

    USER.do_something PROXY.do_something INSTANCE FOLLOWER.new
  44. MY META MOMENTS PUTTER SUMMARY ▸method_missing -> send -> instance

    eval -> define_method ▸prepend -> super -> puts ▸override new -> follow all instances
  45. MY META MOMENTS SO WHAT DO WE DO WITH ALL

    OF THIS? ▸Metaprogramming is like learning another language ▸Contribute to things like Rails, FactoryGirl, or Putter! ▸I made putter, what will you make?
  46. MY META MOMENTS RESOURCES ▸"Writing a Domain-Specific Language in Ruby"

    — Thoughtbot ▸Ruby Under a Microscope - Pat Shaughnessy ▸Metaprogramming Ruby 2 - Paolo Perrotta
  47. MY META MOMENTS PHOTOS ▸ License: https://creativecommons.org/licenses/by/2.0/ ▸ Perks of

    metaprogramming: https://www.flickr.com/photos/jovriens/4956376253 ▸ Putter: https://www.flickr.com/photos/52323788@N06/8759506082 ▸ Send: https://www.flickr.com/photos/katerha/7372776674 ▸ Inheritance: https://www.flickr.com/photos/mujitra/2559447601 ▸ Ancestors: https://www.flickr.com/photos/screenpunk/3814061348 ▸ Prepend: https://www.flickr.com/photos/canad/10019084556 ▸ Method dispatch: https://www.flickr.com/photos/glenbledsoe/6245440290 ▸ Method missing: https://www.flickr.com/photos/katiethebeau/8387139427 ▸ Define method: https://www.flickr.com/photos/124247024@N07/14089978785 ▸ Basic Object: https://www.flickr.com/photos/111441268@N03/15651424294 ▸ Singleton Classes: https://www.flickr.com/photos/gigi62/4287495824 ▸ Instance eval: https://www.flickr.com/photos/ruiwen/3260095534
  48. MY META MOMENTS THANK YOU! ▸Hopefully you have some tools

    to start meta-hacking away! ▸Any questions? ▸Have a great RubyConf!