Metaprogramming
-
Upload
alex-koppel -
Category
Technology
-
view
2.218 -
download
1
description
Transcript of Metaprogramming
![Page 1: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/1.jpg)
Metaprogramming Alex Koppel 6Wunderkinder
Alex Koppel – 1/3/2012
![Page 2: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/2.jpg)
What is metaprogramming?
! "Like programming, only meta" – an old colleague
! Code that writes code
! "Metaprogramming is the writing of computer programs that write or manipulate other programs (or themselves) as their data, or that do part of the work at run time that would otherwise be done at compile time.“ – Wikipedia
Alex Koppel - 6Wunderkinder
![Page 3: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/3.jpg)
Why should you care? ! Ruby allows metaprogramming
! Metaprogramming can create stunningly effective tools ! Rails is metaprogramming
! You want to be a more effective Ruby programmer
6Wunderkinder
![Page 4: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/4.jpg)
Caution! ! Wield metaprogramming deliberately
! Your code will be harder to understand ! Will other developers lose time figuring out your
magic?
! Your code will be more brittle ! Altering other classes = dependency ! Changes to underlying code can break your app ! c.f. Facebooker
6Wunderkinder
![Page 5: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/5.jpg)
Techniques
6Wunderkinder
![Page 6: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/6.jpg)
Reopening Classes ! Add additional methods or variables to existing
classes
! Very simple, very powerful
! New code is executed as your files are loaded ! In a sense, this is a "static" metaprogramming ! Common in monkeypatching
! Caveat: this creates the class if not yet defined
6Wunderkinder
![Page 7: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/7.jpg)
Code require 'active_support/core_ext/string/inflections'!
# reopen String class!class String! # add a module! include ActiveSupport::CoreExtensions::String::Inflections!end!
6Wunderkinder
![Page 8: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/8.jpg)
alias_method ! alias_method(new_name, old_name)
! Makes an copy of a method ! exist v. exists
! Copy remains if the original is overridden ! You may remember alias_method_chain
! Can be used dynamically in other methods
6Wunderkinder
![Page 9: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/9.jpg)
Code class Foo! def bar! 3! end! # make a copy of bar! alias_method :bar, :old_bar! # redefine bar! def bar! 4! end!end!
Foo.new.bar!# => 4!Foo.new.old_bar!# => 3!
6Wunderkinder
![Page 10: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/10.jpg)
Metaclasses ! All Ruby objects have a unique class
! The "metaclass", a.k.a. "eigenclass" ! Classes too ! Special cases: Fixnums, true, false, nil
! You can access this class to change the object ! class << object opens up the metaclass ! def object.new_method and object.instance_eval
also work
! Note: reopening classes and alias_method alter the original class, not a metaclass
6Wunderkinder
![Page 11: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/11.jpg)
What are Objects and Classes, really?
! An object is actually simple[1] ! A collection of instance variables ! A reference to a class
! When you call a method, Ruby goes up the class hierarchy for its definition ! Metaclass, then class, then superclass
! Changing methods on an individual object is really changing its metaclass
6Wunderkinder [1] http://ruby-doc.org/docs/ProgrammingRuby/html/classes.html
![Page 12: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/12.jpg)
Code 1: Classes # Using class << self for a class is effectively!# equivalent to reopening the class (see above)!class Person!! # this can be easier than writing tons of def self.s ! class << self! def species! "Homo Sapien"! end! end!end!
class << Person! def species; "Homo Sapien“; end!end!
Person.species == “Homo Sapien“ # true! 6Wunderkinder
![Page 13: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/13.jpg)
Code 2: Objects str = "hello world"!class << str! def foo! 2! end!end!
str.foo!# => 2!"hello world".foo!# => NoMethodError!
6Wunderkinder
![Page 14: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/14.jpg)
Case study: non-breaking changes
! Example from Koala (Facebook SDK)
! Changing a method to return strings rather than hashes
! Could this be done without breaking apps?
! Use metaprogramming on the returned strings ! Add missing methods ! Print deprecation warning
6Wunderkinder
![Page 15: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/15.jpg)
Code def get_app_access_token! info = get_app_access_token_info # hash! string = info["access_token"]!
# make the string compatible with hash accessors! command = <<-EOS! def [](index)! puts "DEPRECATION WARNING"! hash = #{hash.inspect} ! hash[index]! end! EOS!
# add those methods just to the string! (class << string; self; end).class_eval command!end!
6Wunderkinder
![Page 16: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/16.jpg)
How does it work? ! Get the individual string's metaclass:
! (class << str; self; end) ! Opens the class scope, and returns the (meta)class
! class_eval ! Executes code in the scope of a class (or metaclass) ! class_eval requires a class, not an instance ! String eval can be used for dynamic content ! You can also eval a regular code block
! Objects altered like this are "singletons" ! Has runtime implications
6Wunderkinder
![Page 17: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/17.jpg)
Problem! ! Ruby's Marshal.dump is used to serialize objects
! Marshaling does NOT work with singletons ! Singletons can't be cached!
! Defining instance#_dump doesn't help ! Expects String#load to exist
! Use this carefully in shared code ! You never know what your users will do
6Wunderkinder
![Page 18: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/18.jpg)
Solution! ! Use modules to mix in unique methods
! Ruby knows how to handle this, even for objects module BackwardsCompatible! def [](key)! puts "Got key! #{key}"! end!end!
my_string = "abc”!# objects:extend <=> classes:include !my_string.send(:extend, BackwardsCompatible)!my_string["foo"]!Got key! foo! => nil !Marshal.dump(my_string)! => "\x04\bIe:\x18BackwardsCompatible\"\babc\x06:\x06ET" !
6Wunderkinder
![Page 19: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/19.jpg)
class_eval again
! Ruby pattern: modules updating the including class ! Add before_filters, attrs, etc. ! Could use .send in some cases
! class_eval is easier to read ! Can define dynamic methods ! ActiveSupport::Concern works this way
! class_eval is like reopening a class ! But can be used without knowing the class in advance ! Won't create the class if it doesn't exist
6Wunderkinder
![Page 20: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/20.jpg)
Code module ControllerStuff! def self.included(base)! # execute new code in the scope of! # the including class! base.class_eval do! before_filter :module_method! attr_accessor :module_var! @module_var = 2! end! end!end!
6Wunderkinder
![Page 21: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/21.jpg)
Method Missing ! Intercept calls to non-existent methods
! Make a class's interface dynamic
! Example: ActiveRecord ! Create dynamic finders on the fly ! @user.find_by_name_and_status
! Example: proxy objects
6Wunderkinder
![Page 22: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/22.jpg)
Pseudo-code for ActiveRecord 2.x
def method_missing(method_id, *args, &block)! # is it the right find_by_...format?!! if match(method_id) && match.finder?!! # method_missing is a class method ! self.class_eval %{ # string! # define the method! def self.#{method_id}(*args)! find(:#{finder}, options.merge ! (finder_options))! end! end! # now call the new method! send(method_id, *arguments)! end!end !
6Wunderkinder
![Page 23: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/23.jpg)
Important Note ! method_missing can be much slower
! There are some tricky issues ! respond_to? and beyond ! Solving each problem can create others [1]
! Don't overuse it ! Can you use define_method instead?
6Wunderkinder [1] http://www.slideshare.net/paoloperrotta/the-revenge-of-methodmissing
![Page 24: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/24.jpg)
Reaching into Objects ! send: call any method on an object (public or not)
! Useful for dynamic programming ! Can also be used to break encapsulation ! public_send (1.9) is safer
! instance_variable_get, instance_variable_set: ! Access variables that lack an accessor ! Don't use these
6Wunderkinder
![Page 25: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/25.jpg)
Code # from Koala (older)!# Scope: class with Typhoeous module included!
def self.make_request(path, args, verb)! # dynamically call the appropriate method! # corresponding to the verb requested !! self.send(verb, url, :params => args)!End!
# from Rails!# Scope: a helper class that needs a !# private controller method!
def universal_calculation(arg)! # call the method on the controller! controller.send(:universal_controller, arg)!end ! 6Wunderkinder
![Page 26: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/26.jpg)
tl;dr ! Metaprogramming is awesome
! Don’t overuse it ! But don’t be afraid to use it when appropriate
! Enjoy :)
6Wunderkinder
![Page 27: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/27.jpg)
Resources ! Ruby books
! URLs: ! http://yehudakatz.com/2009/11/15/metaprogramming-
in-ruby-its-all-about-the-self/ ! http://ruby-doc.org/docs/ProgrammingRuby/html/
classes.html ! http://www.slideshare.net/paoloperrotta/the-
revenge-of-methodmissing ! http://pragprog.com/book/ppmetr/metaprogramming-
ruby
! Google "ruby metaprogramming“
6Wunderkinder
![Page 28: Metaprogramming](https://reader033.fdocuments.us/reader033/viewer/2022042814/554f4c1ab4c905b9508b4985/html5/thumbnails/28.jpg)
Questions?
6Wunderkinder
@arsduo @6Wunderkinder