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

meta

Avatar for John DeWyze John DeWyze
November 11, 2016

 meta

My Meta Moments at RubyConf 2016

Avatar for John DeWyze

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!