Your SlideShare is downloading. ×
0
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Metaprogramming in Ruby
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Metaprogramming in Ruby

2,298

Published on

Published in: Technology
0 Comments
6 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,298
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
67
Comments
0
Likes
6
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Metaprogramming in Ruby
  • 2. Things I Wish I KnewWhen I Started Ruby
  • 3. Who?Joshua Hull@jjhttps://github.com/joshbuddy
  • 4. What?
  • 5. What?Writing programs that write programs.
  • 6. What?Writing programs that write programs. NOT code generation!
  • 7. Why?We all do it.
  • 8. Why? We all do it.Ruby attr_accessor :my_attribute
  • 9. Why? We all do it.Ruby attr_accessor :my_attribute def my_attribute @my_attribute end def my_attribute=(my_attribute) @my_attribute = my_attribute end
  • 10. Why? We all do it.Ruby attr_accessor :my_attributeJava public int getAttribute() { return attribute; } public void setAttribute(int newAttribute) { attribute = newAttribute; }
  • 11. Why? We all do it. ner in RubyW attr_accessor :my_attribute Java public int getAttribute() { return attribute; } public void setAttribute(int newAttribute) { attribute = newAttribute; }
  • 12. Why?def age @age || not setenddef gender @gender || not setenddef name @name || not setend
  • 13. Why? def age @age || not set end def gender @gender || not set end def name @name || not set end[:age, :gender, :name].each do |attr| define_method(attr) do instance_variable_get(:"@#{attr}") || not set endend
  • 14. Drawbacks
  • 15. DrawbacksYou can write some difficult-to-understand code.
  • 16. DrawbacksYou can write some difficult-to-understand code.
  • 17. Method dispatch
  • 18. Method dispatch class MyObject attr_accessor :name def say_hello puts "hello" puts name end end
  • 19. Method dispatch class MyObject attr_accessor :name def say_hello puts "hello" puts name end end This is a method call, but who receives it?
  • 20. Method dispatch class MyObject attr_accessor :name def say_hello puts "hello" puts self.name end end
  • 21. Method dispatch class MyObject attr_accessor :name def say_hello puts "hello" puts self.name end end self is always the implied receiver!
  • 22. Method dispatch class MyObject attr_accessor :name def say_hello puts "hello" puts name end end
  • 23. Method dispatch class MyObject self.attr_accessor :name def say_hello puts "hello" puts name end end
  • 24. Method dispatch class MyObjectWho is self self.attr_accessor :name here? def say_hello puts "hello" puts name end end
  • 25. Method dispatch class MyObjectWho is self self.attr_accessor :name here? def say_hello puts "hello"The class puts name end is! end
  • 26. Method dispatch Module#attrWho is self here?The class is!
  • 27. Ruby classes
  • 28. Ruby classesclass NewClass def hey puts hello! endend
  • 29. Ruby classes class NewClass { def hey puts hello! end endThis is normal code!
  • 30. Ruby classesclass NewClass def hey puts hello! endend is the same asNewClass = Class.new do def hey puts hello! endend
  • 31. Ruby classesclass ParsingError < RuntimeErrorend
  • 32. Ruby classesclass ParsingError < RuntimeErrorend is the same asParsingError = Class.new(RuntimeError)
  • 33. Ruby classesdef class_with_accessors(*attributes) Class.new do attr_accessor *attributes endend
  • 34. Ruby classesdef class_with_accessors(*attributes) Class.new do attr_accessor *attributes endend Returns a new class!
  • 35. Ruby classesdef class_with_accessors(*attributes) Class.new do attr_accessor *attributes endend Returns a new class!class Person < class_with_accessors(:name, :age, :sex) # ...end
  • 36. eval, instance_eval, class_eval
  • 37. eval, instance_eval, class_evalMethodevalinstance_evalclass_eval
  • 38. eval, instance_eval, class_evalMethod Contexteval your current contextinstance_eval the objectclass_eval the object’s class
  • 39. eval, instance_eval, class_evalevaleval "puts hello"# hello
  • 40. eval, instance_eval, class_evalinstance_evalclass MyClassendMyClass.instance_eval("def hi; hi; end")
  • 41. eval, instance_eval, class_evalinstance_evalclass MyClassendMyClass.instance_eval("def hi; hi; end")MyClass.hi# hi
  • 42. eval, instance_eval, class_evalinstance_evalclass MyClassendMyClass.instance_eval("def hi; hi end")obj = MyClass.new# <MyClass:0x10178aff8>obj.hi# NoMethodError: undefined method `hi for #<MyClass>
  • 43. eval, instance_eval, class_evalclass_evalclass MyClassendMyClass.class_eval("def hi; hi end")
  • 44. eval, instance_eval, class_evalclass_evalclass MyClassendMyClass.class_eval("def hi; hi end")MyClass.hi# NoMethodError: undefined method `hi for MyClass:Class
  • 45. eval, instance_eval, class_evalclass_evalclass MyClassendMyClass.class_eval("def hi; hi end")obj = MyClass.new# <MyClass:0x101849688>obj.hi# "hi"
  • 46. eval, instance_eval, class_evalclass_eval instance_evalclass MyClass class MyClassend endMyClass.class_eval("def hi; hi; end") MyClass.instance_eval("def hi; hi; end")obj = MyClass.new MyClass.hi# <MyClass:0x101849688> # hiobj.hi# "hi"
  • 47. eval, instance_eval, class_evalNinja’s will attack you if ...you don’t use __FILE__, __LINE__
  • 48. eval, instance_eval, class_evalclass HiThereendHiThere.class_eval "def hi; raise; end"HiThere.class_eval "def hi_with_niceness; raise; end", __FILE__, __LINE__HiThere.new.hiHiThere.new.hi_with_niceness
  • 49. eval, instance_eval, class_evalclass HiThereendHiThere.class_eval "def hi; raise; end"HiThere.class_eval "def hi_with_niceness; raise; end", __FILE__, __LINE__HiThere.new.hiHiThere.new.hi_with_niceness(eval):1:in `hi: unhandled exception from my_file.rb:7my_file.rb:5:in `hi_with_niceness: unhandled exception from my_file.rb:7
  • 50. eval, instance_eval, class_evalclass HiThereendHiThere.class_eval "def hi; raise; end"HiThere.class_eval "def hi_with_niceness; raise; end", __FILE__, __LINE__HiThere.new.hiHiThere.new.hi_with_niceness(eval):1:in `hi: unhandled exception from my_file.rb:7 So nice. <3my_file.rb:5:in `hi_with_niceness: unhandled exception from my_file.rb:7
  • 51. eval, instance_eval, class_eval Implement attr_accessor!
  • 52. eval, instance_eval, class_evalclass Module def create_attr(attribute) class_eval("def #{attribute}; @#{attribute}; end") endend
  • 53. eval, instance_eval, class_evalclass Module def create_attr(attribute) class_eval("def #{attribute}; @#{attribute}; end") endendclass M create_attr :hiend
  • 54. eval, instance_eval, class_evalclass Module def create_attr(attribute) class_eval("def #{attribute}; @#{attribute}; end") endend def Mclass M def hi create_attr :hi @hiend end end
  • 55. Defining methods
  • 56. Defining methods For an objecto = Object.newo.instance_eval("def just_this_object; end")o.just_this_object
  • 57. Defining methods For an objecto = Object.newo.instance_eval("def just_this_object; end")o.just_this_objectObject.new.just_this_object# NoMethodError: undefined method `just_this_object
  • 58. Defining methods For an objecto = Object.newo.instance_eval("def just_this_object; end")o.just_this_objectObject.new.just_this_object# NoMethodError: undefined method `just_this_objecto = Object.newo.instance_eval { def just_this_object end}
  • 59. Defining methods For an object o = Object.new o.extend(Module.new { def just_this_object end })
  • 60. Defining methods For a classMyClass = Class.newclass MyClass def new_method endendMyClass.new.respond_to?(:new_method) # true
  • 61. Defining methods For a classMyClass = Class.newMyClass.class_eval " MyClass.class_eval do def new_method def new_method end end" endMyClass.send(:define_method, :new_method) { # your method body}
  • 62. Defining methods AliasingModule#alias_method
  • 63. Scoping
  • 64. Scopingmodule Project class Main def run # ... end endend
  • 65. Scoping module Project class Main def run # ... end end end Class definitionsModule definitionsMethod definitions
  • 66. Scopinga = hellomodule Project class Main def run puts a end endendProject::Main.new.run
  • 67. Scoping a = hello module Project class Main def run puts a end end end Project::Main.new.run# undefined local variable or method `a for #<Project::Main> (NameError)
  • 68. Scoping module Project class Main end end a = hello Project::Main.class_eval do define_method(:run) do puts a end endProject::Main.new.run # => hello
  • 69. ScopingExample: Connection Sharingmodule AddConnections def self.add_connection_methods(cls, host, port) cls.class_eval do define_method(:get_connection) do puts "Getting connection for #{host}:#{port}" end define_method(:host) { host } define_method(:port) { port } end endend
  • 70. ScopingExample: Connection Sharingmodule AddConnections def self.add_connection_methods(cls, host, port) cls.class_eval do define_method(:get_connection) do puts "Getting connection for #{host}:#{port}" end define_method(:host) { host } define_method(:port) { port } end endendClient = Class.newAddConnections.add_connection_methods(Client, localhost, 8080)Client.new.get_connection # Getting connection for localhost:8080Client.new.host # localhostClient.new.port # 8080
  • 71. ScopingKernel#bindingLet’s you leak the current “bindings”
  • 72. ScopingKernel#bindingLet’s you leak the current “bindings” def create_connection(bind) eval connection = "I am a connection" , bind end connection = nil create_connection(binding) connection # => I am a connection
  • 73. ScopingKernel#bindingLet’s you leak the current “bindings” def create_connection(bind) eval connection = "I am a connection" , bind end Calls connection = nilwith the create_connection(binding)current connection # => I am a connection state
  • 74. ScopingKernel#bindingLet’s you leak the current “bindings” def create_connection(bind) eval connection = "I am a connection" , bind end Calls connection = nilwith the create_connection(binding)current connection # => I am a connection state MAGIC!
  • 75. ScopingKernel#bindingLet’s you leak the current “bindings” def create_connection(bind) eval connection = "I am a connection" , bind end # connection = nil create_connection(binding) connection # undefined local variable or method `connection
  • 76. ScopingKernel#bindingLet’s you leak the current “bindings” def create_connection(bind) eval connection = "I am a connection" , bind end # connection = nil create_connection(binding) connection # undefined local variable or method `connection You can’t add to the local variables via binding
  • 77. ScopingKernel#bindingLet’s you leak the current “bindings” def create_connection(bind) eval connection = "I am a connection" , bind end eval "connection = nil" create_connection(binding) connection # undefined local variable or method `connection You can’t add to the local variables via eval
  • 78. ScopingKernel#bindingTOPLEVEL_BINDING
  • 79. ScopingKernel#bindingTOPLEVEL_BINDING a = hello module Program class Main def run puts eval("a", TOPLEVEL_BINDING) end end end Program::Main.new.run # => hello
  • 80. Interception!(aka lazy magic)
  • 81. Interception!method_missing(method, *args, &blk)
  • 82. Interception!method_missing(method, *args, &blk)class MethodMissing def method_missing(m, *args, &blk) puts "method_missing #{m} #{args.inspect} #{blk.inspect}" super endend
  • 83. Interception!method_missing(method, *args, &blk)class MethodMissing def method_missing(m, *args, &blk) puts "method_missing #{m} #{args.inspect} #{blk.inspect}" super endendmm = MethodMissing.newmm.i_dont_know_this(1, 2, 3)# method_missing i_dont_know_this [1, 2, 3] nil# NoMethodError: undefined method `i_dont_know_this for #<MethodMissing>
  • 84. Interception!Example: Timingmodule MethodsWithTiming def method_missing(m, *args, &blk) if timed_method = m.to_s[/^(.*)_with_timing$/, 1] and respond_to?(timed_method) respond = nil measurement = Benchmark.measure { respond = send(timed_method, *args, &blk) } puts "Method #{m} took #{measurement}" respond else super end endend
  • 85. Interception! Example: Timing module MethodsWithTiming def method_missing(m, *args, &blk) if timed_method = m.to_s[/^(.*)_with_timing$/, 1] and respond_to?(timed_method) respond = nil measurement = Benchmark.measure { respond = send(timed_method, *args, &blk) } puts "Method #{m} took #{measurement}" respond else super end end end sc = SlowClass.newclass SlowClass sc.slow include MethodsWithTiming def slow sleep 1 sc.slow_with_timing end # Method slow_with_timing took 0.000000 0.000000 0.000000 ( 1.000088)end
  • 86. Interception!Example: Proxyclass Proxy def initialize(backing) @backing = backing end def method_missing(m, *args, &blk) @backing.send(m, *args, &blk) endend
  • 87. Interception!Example: Proxyclass LoggingProxy def initialize(backing) @backing = backing end def method_missing(m, *args, &blk) puts "Calling method #{m} with #{args.inspect}" @backing.send(m, *args, &blk) endend
  • 88. Interception!Example: Simple DSL class NameCollector attr_reader :names def initialize @names = [] end def method_missing(method, *args, &blk) args.empty? ? @names.push(method.to_s.capitalize) : super end end nc = NameCollector.new nc.josh nc.bob nc.jane nc.names.join( ) # => Josh Bob Jane
  • 89. Interception!Object#respond_to?(sym)
  • 90. Interception! Object#respond_to?(sym) Example: Timingmodule MethodsWithTiming alias_method :original_respond_to?, :respond_to? def method_missing(m, *args, &blk) if timed_method = m.to_s[/^(.*)_with_timing$/, 1] and original_respond_to?(timed_method) respond = nil measurement = Benchmark.measure { respond = send(timed_method, *args, &blk) } puts "Method #{m} took #{measurement}" respond else super end end def respond_to?(sym) (timed_method = sym.to_s[/^(.*)_with_timing$/, 1]) ? original_respond_to?(timed_method.to_sym) : original_respond_to?(sym) endend
  • 91. Interception! Object#respond_to?(sym) Example: Timingmodule MethodsWithTiming ge ts alias_method :original_respond_to?, :respond_to? It def method_missing(m, *args, &blk) r! if timed_method = m.to_s[/^(.*)_with_timing$/, 1] and original_respond_to?(timed_method) te respond = nil et measurement = Benchmark.measure { b respond = send(timed_method, *args, &blk) } puts "Method #{m} took #{measurement}" respond else super end end def respond_to?(sym) (timed_method = sym.to_s[/^(.*)_with_timing$/, 1]) ? original_respond_to?(timed_method.to_sym) : original_respond_to?(sym) endend
  • 92. Interception! Object#respond_to_missing?(sym) (1.9 only) Example: Timingmodule MethodsWithTiming def method_missing(m, *args, &blk) if timed_method = m.to_s[/^(.*)_with_timing$/, 1] and respond_to?(timed_method) respond = nil measurement = Benchmark.measure { respond = send(timed_method, *args, &blk) } puts "Method #{m} took #{measurement}" respond else super end end def respond_to_missing?(sym) (timed_method = sym.to_s[/^(.*)_with_timing$/, 1]) ? respond_to?(timed_method.to_sym) : super endend
  • 93. Interception!const_missing(sym)
  • 94. Interception!const_missing(sym)MyClass::MyOtherClass# MyClass.const_missing(:MyOtherClass)
  • 95. Interception!const_missing(sym)MyClass::MyOtherClass# MyClass.const_missing(:MyOtherClass)Example: Loaderclass Loader def self.const_missing(sym) file = File.join(File.dirname(__FILE__), "#{sym.to_s.downcase}.rb") if File.exist?(file) require file Object.const_defined?(sym) ? Object.const_get(sym) : super else puts "cant find #{file}, sorry!" super end endend
  • 96. Interception!Example: Loaderclass Loader def self.const_missing(sym) file = File.join(File.dirname(__FILE__), "#{sym.to_s.downcase}.rb") if File.exist?(file) require file Object.const_defined?(sym) ? Object.const_get(sym) : super else puts "cant find #{file}, sorry!" super end endend
  • 97. Interception!Example: Loaderclass Loader def self.const_missing(sym) file = File.join(File.dirname(__FILE__), "#{sym.to_s.downcase}.rb") if File.exist?(file) require file Object.const_defined?(sym) ? Object.const_get(sym) : super else puts "cant find #{file}, sorry!" super end endendLoader::Auto# cant find ./auto.rb, sorry!# NameError: uninitialized constant Loader::Auto# or, if you have an ./auto.rbLoader::Auto# => Auto
  • 98. Callbacks
  • 99. CallbacksModule#method_added
  • 100. CallbacksModule#method_added
  • 101. CallbacksModule#method_addedclass MyClass def self.method_added(m) puts "adding #{m}" end puts "defining my method" def my_method two end puts "done defining my method"end
  • 102. CallbacksModule#method_addedclass MyClass defining my method def self.method_added(m) adding my_method puts "adding #{m}" done defining my method end puts "defining my method" def my_method two end puts "done defining my method"end
  • 103. CallbacksModule#method_addedExample: Thor!class Tasks def self.desc(desc) @desc = desc end def self.method_added(m) (@method_descs ||= {})[m] = @desc @desc = nil end def self.method_description(m) method_defined?(m) ? @method_descs[m] || "This action isnt documented" : "This action doesnt exist" end desc "Start server" def start end def stop endend
  • 104. CallbacksModule#method_addedExample: Thor! Record the descriptionclass Tasks def self.desc(desc) @desc = desc When a method is added, end record the description associated def self.method_added(m) with that method (@method_descs ||= {})[m] = @desc @desc = nil end Provide the description for a def self.method_description(m) method, or, if not found, some method_defined?(m) ? default string. @method_descs[m] || "This action isnt documented" : "This action doesnt exist" end desc "Start server" def start end def stop endend
  • 105. CallbacksModule#method_addedExample: Thor! Record the descriptionclass Tasks def self.desc(desc) @desc = desc When a method is added, end record the description associated def self.method_added(m) with that method (@method_descs ||= {})[m] = @desc @desc = nil end Provide the description for a def self.method_description(m) method, or, if not found, some method_defined?(m) ? default string. @method_descs[m] || "This action isnt documented" : "This action doesnt exist" end desc "Start server" def start end Described! def stop endend
  • 106. CallbacksModule#method_addedExample: Thor! Query your methods!class Tasks puts Tasks.method_description(:start) def self.desc(desc) # => Start server @desc = desc puts Tasks.method_description(:stop) end # => This action isnt documented def self.method_added(m) puts Tasks.method_description(:restart) (@method_descs ||= {})[m] = @desc # => This action doesnt exist @desc = nil end def self.method_description(m) method_defined?(m) ? @method_descs[m] || "This action isnt documented" : "This action doesnt exist" end desc "Start server" def start end def stop endend
  • 107. CallbacksObject#singleton_method_added
  • 108. CallbacksObject#singleton_method_addedclass ClassWithMethods def self.singleton_method_added(m) puts "ADDING! #{m}" end def self.another endend
  • 109. CallbacksObject#singleton_method_addedclass ClassWithMethods def self.singleton_method_added(m) puts "ADDING! #{m}" end def self.another endend# ADDING! singleton_method_added# ADDING! another
  • 110. CallbacksObject#singleton_method_addedclass ClassWithMethods def self.singleton_method_added(m) puts "ADDING! #{m}" end def self.another endend Holy meta!# ADDING! singleton_method_added# ADDING! another
  • 111. CallbacksModule#includedmodule Logger def self.included(m) puts "adding logging to #{m}" endendclass Server include Loggerend# adding logging to Server
  • 112. CallbacksModule#includedExample: ClassMethods patternmodule Logger class Server def self.included(m) include Logger puts "adding logging to #{m}" end def self.create log("Creating server!") def self.log(message) end puts "LOG: #{message}" end endendServer.create# `create: undefined method `log for Server:Class (NoMethodError)
  • 113. CallbacksModule#includedExample: ClassMethods patternmodule Logger class Server def self.included(m) include Logger m.extend(ClassMethods) end def self.create log("Creating server!") module ClassMethods end def log(message) end puts "LOG: #{message}" end endendServer.create# LOG: Creating server!
  • 114. CallbacksModule#extendedmodule One def self.extended(obj) puts "#{self} has been extended by #{obj}" endendObject.new.extend(One)# One has been extended by #<Object:0x1019614a8>
  • 115. CallbacksClass#inheritedclass Parent def self.inherited(o) puts "#{self} was inherited by #{o}" endendclass Child < Parentend# Parent was inherited by Child
  • 116. CallbacksGuarding callbacksModule#append_features includeModule#extend_object extend def self.extend_object(o) super end def self.append_features(o) super end
  • 117. CallbacksGuarding callbacksModule#append_features includeModule#extend_object extenddef self.append_features(o) o.instance_method(:<=>) ? super : warn(you no can uze)end
  • 118. CallbacksKernel#callerdef one twoenddef two method name three file name line (optional)enddef three p callerend# ["method.rb:156:in `two", "method.rb:152:in `one", "method.rb:163"] https://github.com/joshbuddy/callsite
  • 119. CallbacksModule#nestingmodule A module B module C p Module.nesting end endend# [A::B::C, A::B, A]
  • 120. There and back again, a parsing tale
  • 121. There and back again, a parsing tale gem install ruby_parser gem install sexp_processor gem install ruby2ruby Let’s go!
  • 122. There and back again, a parsing taleParsing require rubygems require ruby_parser RubyParser.new.process("string") s(:str, "string") Type Arguments...
  • 123. There and back again, a parsing taleParsing require rubygems require ruby_parser RubyParser.new.process("string") s(:str, "string") [:str, "string"] # Sexp Sexp.superclass # Array
  • 124. There and back again, a parsing taleParsing RubyParser.new.process("string + string")s(:call, s(:str, "string"), :+, s(:arglist, s(:str, "string"))) Method Method Receiver Arguments call name
  • 125. There and back again, a parsing taleParsing RubyParser.new.process("string + string") s(:call, nil, :puts, s(:arglist, s(:str, "hello world"))) Method Receiver Method Arguments call name
  • 126. There and back again, a parsing taleAnd, back again... require rubygems require ruby2ruby Ruby2Ruby.new.process [:str, "hello"] # => "hello"
  • 127. There and back again, a parsing taleAnd, back again... require rubygems require ruby2ruby Ruby2Ruby.new.process [:str, "hello"] # => "hello" Ruby2Ruby.new.process [:lit, :symbol] # => :symbol
  • 128. There and back again, a parsing taleRoundtrip require sexp_processor require ruby2ruby require ruby_parser class JarJarify < SexpProcessor def initialize self.strict = false super end def process_str(str) new_string = "YOUZA GONNA SAY #{str[-1]}" str.clear s(:str, new_string) end end
  • 129. There and back again, a parsing taleRoundtrip class JarJarify < SexpProcessor def initialize self.strict = false super end def process_str(str) new_string = "YOUZA GONNA SAY #{str[-1]}" str.clear s(:str, new_string) end end ast = RubyParser.new.process(puts "hello") Ruby2Ruby.new.process(JarJarify.new.process(ast)) # => puts("YOUZA GONNA SAY hello")
  • 130. There and back again, a parsing taleRoundtrip class JarJarify < SexpProcessor def initialize self.strict = false Process type :str super end def process_str(str) new_string = "YOUZA GONNA SAY #{str[-1]}" str.clear s(:str, new_string) Consume the current sexp end end Return a new one ast = RubyParser.new.process(puts "hello") Ruby2Ruby.new.process(JarJarify.new.process(ast)) # => puts("YOUZA GONNA SAY hello")
  • 131. IT’S OVER!

×