SlideShare ist ein Scribd-Unternehmen logo
1 von 133
Downloaden Sie, um offline zu lesen
Metaprogramming
    in Ruby
Things I Wish I Knew
When I Started Ruby
Who?
Joshua Hull
@jj
https://github.com/joshbuddy
What?
What?


Writing programs that write programs.
What?


Writing programs that write programs.
        NOT code generation!
Why?
We all do it.
Why?
           We all do it.
Ruby   attr_accessor :my_attribute
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
Why?
           We all do it.
Ruby   attr_accessor :my_attribute



Java   public int getAttribute() {
         return attribute;
       }

       public void setAttribute(int newAttribute) {
         attribute = newAttribute;
       }
Why?
               We all do it.
   ner
 in Ruby
W          attr_accessor :my_attribute



    Java   public int getAttribute() {
             return attribute;
           }

           public void setAttribute(int newAttribute) {
             attribute = newAttribute;
           }
Why?
def age
  @age || 'not set'
end

def gender
  @gender || 'not set'
end

def name
  @name || 'not set'
end
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'
  end
end
Drawbacks
Drawbacks
You can write some difïŹcult-to-understand code.
Drawbacks
You can write some difïŹcult-to-understand code.
Method dispatch
Method dispatch

   class MyObject
     attr_accessor :name

     def say_hello
       puts "hello"
       puts name
     end
   end
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?
Method dispatch

   class MyObject
     attr_accessor :name

     def say_hello
       puts "hello"
       puts self.name
     end
   end
Method dispatch

   class MyObject
     attr_accessor :name

     def say_hello
       puts "hello"
       puts self.name
     end
   end
                           self is always the
                            implied receiver!
Method dispatch

   class MyObject
     attr_accessor :name

     def say_hello
       puts "hello"
       puts name
     end
   end
Method dispatch

  class MyObject
    self.attr_accessor :name

    def say_hello
      puts "hello"
      puts name
    end
  end
Method dispatch

              class MyObject

Who is self     self.attr_accessor :name

 here?          def say_hello
                  puts "hello"
                  puts name
                end
              end
Method dispatch

              class MyObject

Who is self     self.attr_accessor :name

  here?         def say_hello
                  puts "hello"
The class         puts name
                end
   is!        end
Method dispatch
              Module#attr

Who is self
  here?
The class
   is!
Ruby classes
Ruby classes
class NewClass
  def hey
    puts 'hello!'
  end
end
Ruby classes
                 class NewClass


             {
                   def hey
                     puts 'hello!'
                   end
                 end
This is normal
    code!
Ruby classes
class NewClass
  def hey
    puts 'hello!'
  end
end

     is the same as

NewClass = Class.new do
  def hey
    puts 'hello!'
  end
end
Ruby classes

class ParsingError < RuntimeError
end
Ruby classes

class ParsingError < RuntimeError
end

            is the same as


ParsingError = Class.new(RuntimeError)
Ruby classes
def class_with_accessors(*attributes)
  Class.new do
    attr_accessor *attributes
  end
end
Ruby classes
def class_with_accessors(*attributes)
  Class.new do
    attr_accessor *attributes
  end
end

                     Returns a new class!
Ruby classes
def class_with_accessors(*attributes)
  Class.new do
    attr_accessor *attributes
  end
end

                     Returns a new class!

class Person < class_with_accessors(:name, :age, :sex)
  # ...
end
eval, instance_eval,
     class_eval
eval, instance_eval,
        class_eval
Method
eval

instance_eval

class_eval
eval, instance_eval,
        class_eval
Method          Context
eval            your current context

instance_eval   the object

class_eval      the object’s class
eval, instance_eval,
       class_eval
eval
eval "puts 'hello'"

# hello
eval, instance_eval,
        class_eval
instance_eval
class MyClass
end

MyClass.instance_eval("def hi; 'hi'; end")
eval, instance_eval,
        class_eval
instance_eval
class MyClass
end

MyClass.instance_eval("def hi; 'hi'; end")



MyClass.hi
# 'hi'
eval, instance_eval,
        class_eval
instance_eval
class MyClass
end

MyClass.instance_eval("def hi; 'hi' end")



obj = MyClass.new
# <MyClass:0x10178aff8>
obj.hi
# NoMethodError: undefined method `hi' for #<MyClass>
eval, instance_eval,
        class_eval
class_eval
class MyClass
end

MyClass.class_eval("def hi; 'hi' end")
eval, instance_eval,
        class_eval
class_eval
class MyClass
end

MyClass.class_eval("def hi; 'hi' end")


MyClass.hi
# NoMethodError: undefined method `hi' for MyClass:Class
eval, instance_eval,
        class_eval
class_eval
class MyClass
end

MyClass.class_eval("def hi; 'hi' end")


obj = MyClass.new
# <MyClass:0x101849688>
obj.hi
# "hi"
eval, instance_eval,
                     class_eval
class_eval                                instance_eval
class MyClass                             class MyClass
end                                       end

MyClass.class_eval("def hi; 'hi'; end")   MyClass.instance_eval("def hi; 'hi'; end")


obj = MyClass.new                         MyClass.hi
# <MyClass:0x101849688>                   # 'hi'
obj.hi
# "hi"
eval, instance_eval,
       class_eval

Ninja’s will attack you if ...
you don’t use __FILE__, __LINE__
eval, instance_eval,
               class_eval
class HiThere
end

HiThere.class_eval "def hi; raise; end"
HiThere.class_eval "def hi_with_niceness; raise; end", __FILE__, __LINE__

HiThere.new.hi
HiThere.new.hi_with_niceness
eval, instance_eval,
               class_eval
class HiThere
end

HiThere.class_eval "def hi; raise; end"
HiThere.class_eval "def hi_with_niceness; raise; end", __FILE__, __LINE__

HiThere.new.hi
HiThere.new.hi_with_niceness


(eval):1:in `hi': unhandled exception
  from my_file.rb:7


my_file.rb:5:in `hi_with_niceness': unhandled exception
  from my_file.rb:7
eval, instance_eval,
               class_eval
class HiThere
end

HiThere.class_eval "def hi; raise; end"
HiThere.class_eval "def hi_with_niceness; raise; end", __FILE__, __LINE__

HiThere.new.hi
HiThere.new.hi_with_niceness


(eval):1:in `hi': unhandled exception
  from my_file.rb:7
                                              So nice. <3

my_file.rb:5:in `hi_with_niceness': unhandled exception
  from my_file.rb:7
eval, instance_eval,
     class_eval

  Implement attr_accessor!
eval, instance_eval,
           class_eval
class Module
  def create_attr(attribute)
    class_eval("def #{attribute}; @#{attribute}; end")
  end
end
eval, instance_eval,
           class_eval
class Module
  def create_attr(attribute)
    class_eval("def #{attribute}; @#{attribute}; end")
  end
end

class M
  create_attr :hi
end
eval, instance_eval,
           class_eval
class Module
  def create_attr(attribute)
    class_eval("def #{attribute}; @#{attribute}; end")
  end
end
                                   def M
class M                              def hi
  create_attr :hi                      @hi
end                                  end
                                   end
DeïŹning methods
DeïŹning methods
              For an object
o = Object.new
o.instance_eval("def just_this_object; end")
o.just_this_object
DeïŹning methods
              For an object
o = Object.new
o.instance_eval("def just_this_object; end")
o.just_this_object


Object.new.just_this_object
# NoMethodError: undefined method `just_this_object'
DeïŹning methods
              For an object
o = Object.new
o.instance_eval("def just_this_object; end")
o.just_this_object


Object.new.just_this_object
# NoMethodError: undefined method `just_this_object'


o = Object.new
o.instance_eval {
  def just_this_object
  end
}
DeïŹning methods
      For an object

   o = Object.new
   o.extend(Module.new {
      def just_this_object
      end
   })
DeïŹning methods
               For a class

MyClass = Class.new


class MyClass
  def new_method
  end
end


MyClass.new.respond_to?(:new_method) # true
DeïŹning methods
                For a class

MyClass = Class.new

MyClass.class_eval "     MyClass.class_eval do
  def new_method           def new_method
  end                      end
"                        end


MyClass.send(:define_method, :new_method) {
  # your method body
}
DeïŹning methods
                      Aliasing
Module#alias_method
Scoping
Scoping
module Project
  class Main
    def run
      # ...
    end
  end
end
Scoping
   module Project
     class Main
       def run
         # ...
       end
     end
   end


 Class deïŹnitions
Module deïŹnitions
Method deïŹnitions
Scoping
a = 'hello'

module Project
  class Main
    def run
      puts a
    end
  end
end

Project::Main.new.run
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)
Scoping
  module Project
    class Main
    end
  end

  a = 'hello'

  Project::Main.class_eval do
    define_method(:run) do
      puts a
    end
  end



Project::Main.new.run # => hello
Scoping
Example: Connection Sharing
module 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
  end
end
Scoping
Example: Connection Sharing
module 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
  end
end

Client = Class.new
AddConnections.add_connection_methods(Client, 'localhost', 8080)

Client.new.get_connection # Getting connection for localhost:8080
Client.new.host           # localhost
Client.new.port           # 8080
Scoping
Kernel#binding
Let’s you leak the current “bindings”
Scoping
Kernel#binding
Let’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
Scoping
Kernel#binding
Let’s you leak the current “bindings”

                def create_connection(bind)
                  eval '
                    connection = "I am a connection"
                  ', bind
                end

 Calls          connection = nil
with the        create_connection(binding)
current         connection # => I am a connection
 state
Scoping
Kernel#binding
Let’s you leak the current “bindings”

                def create_connection(bind)
                  eval '
                    connection = "I am a connection"
                  ', bind
                end

 Calls          connection = nil
with the        create_connection(binding)
current         connection # => I am a connection
 state
                                        MAGIC!
Scoping
Kernel#binding
Let’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'
Scoping
Kernel#binding
Let’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
Scoping
Kernel#binding
Let’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
Scoping
Kernel#binding
TOPLEVEL_BINDING
Scoping
Kernel#binding
TOPLEVEL_BINDING
       a = 'hello'

       module Program
         class Main
           def run
             puts eval("a", TOPLEVEL_BINDING)
           end
         end
       end

       Program::Main.new.run # => hello
Interception!
(aka lazy magic)
Interception!
method_missing(method, *args, &blk)
Interception!
method_missing(method, *args, &blk)
class MethodMissing
  def method_missing(m, *args, &blk)
    puts "method_missing #{m} #{args.inspect} #{blk.inspect}"
    super
  end
end
Interception!
method_missing(method, *args, &blk)
class MethodMissing
  def method_missing(m, *args, &blk)
    puts "method_missing #{m} #{args.inspect} #{blk.inspect}"
    super
  end
end

mm = MethodMissing.new
mm.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>
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
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.new
class 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
Interception!
Example: Proxy
class Proxy
  def initialize(backing)
    @backing = backing
  end

  def method_missing(m, *args, &blk)
    @backing.send(m, *args, &blk)
  end
end
Interception!
Example: Proxy
class 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)
  end
end
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
Interception!
Object#respond_to?(sym)
Interception!
   Object#respond_to?(sym)
   Example: Timing
module 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)
  end
end
Interception!
   Object#respond_to?(sym)
   Example: Timing
module 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)
  end
end
Interception!
   Object#respond_to_missing?(sym) (1.9 only)
   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

  def respond_to_missing?(sym)
    (timed_method = sym.to_s[/^(.*)_with_timing$/, 1]) ?
      respond_to?(timed_method.to_sym) :
      super
  end
end
Interception!
const_missing(sym)
Interception!
const_missing(sym)
MyClass::MyOtherClass

# MyClass.const_missing(:MyOtherClass)
Interception!
const_missing(sym)
MyClass::MyOtherClass

# MyClass.const_missing(:MyOtherClass)

Example: Loader
class 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 "can't find #{file}, sorry!"
      super
    end
  end
end
Interception!
Example: Loader
class 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 "can't find #{file}, sorry!"
      super
    end
  end
end
Interception!
Example: Loader
class 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 "can't find #{file}, sorry!"
      super
    end
  end
end


Loader::Auto
# can't find ./auto.rb, sorry!
# NameError: uninitialized constant Loader::Auto

# or, if you have an ./auto.rb
Loader::Auto
# => Auto
Callbacks
Callbacks
Module#method_added
Callbacks
Module#method_added
Callbacks
Module#method_added
class 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
Callbacks
Module#method_added
class 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
Callbacks
Module#method_added
Example: 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 isn't documented" :
      "This action doesn't exist"
  end

 desc "Start server"
 def start
 end

  def stop
  end
end
Callbacks
Module#method_added
Example: Thor!                                               Record the description
class 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 isn't documented" :
      "This action doesn't exist"
  end

 desc "Start server"
 def start
 end

  def stop
  end
end
Callbacks
Module#method_added
Example: Thor!                                               Record the description
class 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 isn't documented" :
      "This action doesn't exist"
  end

 desc "Start server"
 def start
 end
                        Described!
  def stop
  end
end
Callbacks
Module#method_added
Example: 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 isn't documented
  def self.method_added(m)                                   puts   Tasks.method_description(:restart)
    (@method_descs ||= {})[m] = @desc                        # =>   This action doesn't exist
    @desc = nil
  end

  def self.method_description(m)
    method_defined?(m) ?
      @method_descs[m] || "This action isn't documented" :
      "This action doesn't exist"
  end

 desc "Start server"
 def start
 end

  def stop
  end
end
Callbacks
Object#singleton_method_added
Callbacks
Object#singleton_method_added
class ClassWithMethods
  def self.singleton_method_added(m)
    puts "ADDING! #{m}"
  end

  def self.another
  end
end
Callbacks
Object#singleton_method_added
class ClassWithMethods
  def self.singleton_method_added(m)
    puts "ADDING! #{m}"
  end

  def self.another
  end
end

# ADDING! singleton_method_added
# ADDING! another
Callbacks
Object#singleton_method_added
class ClassWithMethods
  def self.singleton_method_added(m)
    puts "ADDING! #{m}"
  end

  def self.another
  end
end
                                       Holy meta!
# ADDING! singleton_method_added
# ADDING! another
Callbacks
Module#included
module Logger
  def self.included(m)
    puts "adding logging to #{m}"
  end
end

class Server
  include Logger
end


# adding logging to Server
Callbacks
Module#included
Example: ClassMethods pattern
module 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
  end
end


Server.create
# `create': undefined method `log' for Server:Class (NoMethodError)
Callbacks
Module#included
Example: ClassMethods pattern
module 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
  end
end

Server.create
# LOG: Creating server!
Callbacks
Module#extended
module One
  def self.extended(obj)
    puts "#{self} has been extended by #{obj}"
  end
end

Object.new.extend(One)



# One has been extended by #<Object:0x1019614a8>
Callbacks
Class#inherited
class Parent
  def self.inherited(o)
    puts "#{self} was inherited by #{o}"
  end
end

class Child < Parent
end


# Parent was inherited by Child
Callbacks
Guarding callbacks
Module#append_features         include
Module#extend_object           extend
 def self.extend_object(o)
   super
 end

 def self.append_features(o)
   super
 end
Callbacks
Guarding callbacks
Module#append_features                     include
Module#extend_object                       extend
def self.append_features(o)
  o.instance_method(:<=>) ? super : warn('you no can uze')
end
Callbacks
Kernel#caller
def one
  two
end

def two
                                                   method name
  three
                 ïŹle name            line              (optional)
end

def three
  p caller
end

# ["method.rb:156:in `two'", "method.rb:152:in `one'", "method.rb:163"]




             https://github.com/joshbuddy/callsite
Callbacks
Module#nesting
module A
  module B
    module C
      p Module.nesting
    end
  end
end

# [A::B::C, A::B, A]
There and back again, a
     parsing tale
There and back again, a
     parsing tale

   gem install ruby_parser
   gem install sexp_processor
   gem install ruby2ruby


          Let’s go!
There and back again, a
               parsing tale
Parsing
            require 'rubygems'
            require 'ruby_parser'
            RubyParser.new.process("'string'")



                     s(:str, "string")



              Type             Arguments...
There and back again, a
               parsing tale
Parsing
            require 'rubygems'
            require 'ruby_parser'
            RubyParser.new.process("'string'")




                s(:str, "string")
                [:str, "string"]    # Sexp

                Sexp.superclass
                # Array
There and back again, a
                parsing tale
Parsing

          RubyParser.new.process("'string' + 'string'")




s(:call, s(:str, "string"), :+, s(:arglist, s(:str, "string")))

 Method                    Method
               Receiver                   Arguments
  call                      name
There and back again, a
                parsing tale
Parsing

          RubyParser.new.process("'string' + 'string'")




  s(:call, nil, :puts, s(:arglist, s(:str, "hello world")))

  Method    Receiver   Method
                                     Arguments
   call                 name
There and back again, a
              parsing tale
And, back again...

      require 'rubygems'
      require 'ruby2ruby'

      Ruby2Ruby.new.process [:str, "hello"] # => "hello"
There and back again, a
              parsing tale
And, back again...

      require 'rubygems'
      require 'ruby2ruby'

      Ruby2Ruby.new.process [:str, "hello"] # => "hello"
      Ruby2Ruby.new.process [:lit, :symbol] # => :symbol
There and back again, a
                parsing tale
Roundtrip
 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
There and back again, a
               parsing tale
Roundtrip
 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")
There and back again, a
               parsing tale
Roundtrip
 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")
IT’S OVER!

Weitere Àhnliche Inhalte

Was ist angesagt?

Javascript
JavascriptJavascript
JavascriptAditya Gaur
 
Java best practices
Java best practicesJava best practices
Java best practicesRay Toal
 
JQuery
JQueryJQuery
JQueryxkarloslk
 
Only oop
Only oopOnly oop
Only oopanitarooge
 
Introduction to java
Introduction to javaIntroduction to java
Introduction to javarishi ram khanal
 
Procedure Typing for Scala
Procedure Typing for ScalaProcedure Typing for Scala
Procedure Typing for Scalaakuklev
 
PHP - Introduction to Object Oriented Programming with PHP
PHP -  Introduction to  Object Oriented Programming with PHPPHP -  Introduction to  Object Oriented Programming with PHP
PHP - Introduction to Object Oriented Programming with PHPVibrant Technologies & Computers
 
Java script basic
Java script basicJava script basic
Java script basicRavi Bhadauria
 
Easymock Tutorial
Easymock TutorialEasymock Tutorial
Easymock TutorialSbin m
 
Swift Programming - Part 2
Swift Programming - Part 2Swift Programming - Part 2
Swift Programming - Part 2Mindfire Solutions
 
Object oreinted php | OOPs
Object oreinted php | OOPsObject oreinted php | OOPs
Object oreinted php | OOPsRavi Bhadauria
 
09 Object Oriented Programming in PHP #burningkeyboards
09 Object Oriented Programming in PHP #burningkeyboards09 Object Oriented Programming in PHP #burningkeyboards
09 Object Oriented Programming in PHP #burningkeyboardsDenis Ristic
 
Clean code slide
Clean code slideClean code slide
Clean code slideAnh Huan Miu
 
Building DSLs With Eclipse
Building DSLs With EclipseBuilding DSLs With Eclipse
Building DSLs With EclipsePeter Friese
 

Was ist angesagt? (20)

Easy mockppt
Easy mockpptEasy mockppt
Easy mockppt
 
Javascript
JavascriptJavascript
Javascript
 
Java Script Best Practices
Java Script Best PracticesJava Script Best Practices
Java Script Best Practices
 
Scala test
Scala testScala test
Scala test
 
J query
J queryJ query
J query
 
Java best practices
Java best practicesJava best practices
Java best practices
 
JQuery
JQueryJQuery
JQuery
 
Only oop
Only oopOnly oop
Only oop
 
Introduction to java
Introduction to javaIntroduction to java
Introduction to java
 
Procedure Typing for Scala
Procedure Typing for ScalaProcedure Typing for Scala
Procedure Typing for Scala
 
PHP - Introduction to Object Oriented Programming with PHP
PHP -  Introduction to  Object Oriented Programming with PHPPHP -  Introduction to  Object Oriented Programming with PHP
PHP - Introduction to Object Oriented Programming with PHP
 
Python advance
Python advancePython advance
Python advance
 
Java script basic
Java script basicJava script basic
Java script basic
 
Easymock Tutorial
Easymock TutorialEasymock Tutorial
Easymock Tutorial
 
Swift Programming - Part 2
Swift Programming - Part 2Swift Programming - Part 2
Swift Programming - Part 2
 
Object oreinted php | OOPs
Object oreinted php | OOPsObject oreinted php | OOPs
Object oreinted php | OOPs
 
SWIFT 3
SWIFT 3SWIFT 3
SWIFT 3
 
09 Object Oriented Programming in PHP #burningkeyboards
09 Object Oriented Programming in PHP #burningkeyboards09 Object Oriented Programming in PHP #burningkeyboards
09 Object Oriented Programming in PHP #burningkeyboards
 
Clean code slide
Clean code slideClean code slide
Clean code slide
 
Building DSLs With Eclipse
Building DSLs With EclipseBuilding DSLs With Eclipse
Building DSLs With Eclipse
 

Andere mochten auch

Security 202 - Are you sure your site is secure?
Security 202 - Are you sure your site is secure?Security 202 - Are you sure your site is secure?
Security 202 - Are you sure your site is secure?ConFoo
 
The business behind open source
The business behind open sourceThe business behind open source
The business behind open sourceConFoo
 
Scalable Architecture 101
Scalable Architecture 101Scalable Architecture 101
Scalable Architecture 101ConFoo
 
Writing a Ruby Gem for beginners
Writing a Ruby Gem for beginnersWriting a Ruby Gem for beginners
Writing a Ruby Gem for beginnersConFoo
 
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+ConFoo
 
Anatomy of a large Django site
Anatomy of a large Django siteAnatomy of a large Django site
Anatomy of a large Django siteConFoo
 
Opensource Authentication and Authorization
Opensource Authentication and AuthorizationOpensource Authentication and Authorization
Opensource Authentication and AuthorizationConFoo
 

Andere mochten auch (7)

Security 202 - Are you sure your site is secure?
Security 202 - Are you sure your site is secure?Security 202 - Are you sure your site is secure?
Security 202 - Are you sure your site is secure?
 
The business behind open source
The business behind open sourceThe business behind open source
The business behind open source
 
Scalable Architecture 101
Scalable Architecture 101Scalable Architecture 101
Scalable Architecture 101
 
Writing a Ruby Gem for beginners
Writing a Ruby Gem for beginnersWriting a Ruby Gem for beginners
Writing a Ruby Gem for beginners
 
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+Marrow: A Meta-Framework for Python 2.6+ and 3.1+
Marrow: A Meta-Framework for Python 2.6+ and 3.1+
 
Anatomy of a large Django site
Anatomy of a large Django siteAnatomy of a large Django site
Anatomy of a large Django site
 
Opensource Authentication and Authorization
Opensource Authentication and AuthorizationOpensource Authentication and Authorization
Opensource Authentication and Authorization
 

Ähnlich wie Metaprogramming in Ruby

The Dark Art of Rails Plugins (2008)
The Dark Art of Rails Plugins (2008)The Dark Art of Rails Plugins (2008)
The Dark Art of Rails Plugins (2008)lazyatom
 
Ruby Metaprogramming
Ruby MetaprogrammingRuby Metaprogramming
Ruby MetaprogrammingNando Vieira
 
Metaprogramming 101
Metaprogramming 101Metaprogramming 101
Metaprogramming 101Nando Vieira
 
Ruby: Beyond the Basics
Ruby: Beyond the BasicsRuby: Beyond the Basics
Ruby: Beyond the BasicsMichael Koby
 
A linguagem de programação Ruby - Robson "Duda" Sejan Soares Dornelles
A linguagem de programação Ruby - Robson "Duda" Sejan Soares DornellesA linguagem de programação Ruby - Robson "Duda" Sejan Soares Dornelles
A linguagem de programação Ruby - Robson "Duda" Sejan Soares DornellesTchelinux
 
Ruby Programming Language
Ruby Programming LanguageRuby Programming Language
Ruby Programming LanguageDuda Dornelles
 
Ruby çš‹ćŒèȘžèš€ć…„é–€ć°ŽèŠœ
Ruby çš‹ćŒèȘžèš€ć…„é–€ć°ŽèŠœRuby çš‹ćŒèȘžèš€ć…„é–€ć°ŽèŠœ
Ruby çš‹ćŒèȘžèš€ć…„é–€ć°ŽèŠœWen-Tien Chang
 
Dsl
DslDsl
Dslphoet
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everythingnoelrap
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Railsrstankov
 
Ruby for C# Developers
Ruby for C# DevelopersRuby for C# Developers
Ruby for C# DevelopersCory Foy
 
Postobjektové programovanie v Ruby
Postobjektové programovanie v RubyPostobjektové programovanie v Ruby
Postobjektové programovanie v RubyJano Suchal
 
Ruby Metaprogramming
Ruby MetaprogrammingRuby Metaprogramming
Ruby MetaprogrammingThaichor Seng
 
Ruby singleton class
Ruby singleton classRuby singleton class
Ruby singleton classAnton Narusberg
 
Metaprogramovanie #1
Metaprogramovanie #1Metaprogramovanie #1
Metaprogramovanie #1Jano Suchal
 
Selfish presentation - ruby internals
Selfish presentation - ruby internalsSelfish presentation - ruby internals
Selfish presentation - ruby internalsWojciech Widenka
 
Self, Class and Module
Self, Class and ModuleSelf, Class and Module
Self, Class and ModuleGourav Tiwari
 
Lightning talk
Lightning talkLightning talk
Lightning talknpalaniuk
 

Ähnlich wie Metaprogramming in Ruby (20)

The Dark Art of Rails Plugins (2008)
The Dark Art of Rails Plugins (2008)The Dark Art of Rails Plugins (2008)
The Dark Art of Rails Plugins (2008)
 
Ruby Metaprogramming
Ruby MetaprogrammingRuby Metaprogramming
Ruby Metaprogramming
 
Metaprogramming 101
Metaprogramming 101Metaprogramming 101
Metaprogramming 101
 
Ruby: Beyond the Basics
Ruby: Beyond the BasicsRuby: Beyond the Basics
Ruby: Beyond the Basics
 
A linguagem de programação Ruby - Robson "Duda" Sejan Soares Dornelles
A linguagem de programação Ruby - Robson "Duda" Sejan Soares DornellesA linguagem de programação Ruby - Robson "Duda" Sejan Soares Dornelles
A linguagem de programação Ruby - Robson "Duda" Sejan Soares Dornelles
 
Ruby Programming Language
Ruby Programming LanguageRuby Programming Language
Ruby Programming Language
 
Ruby
RubyRuby
Ruby
 
Ruby çš‹ćŒèȘžèš€ć…„é–€ć°ŽèŠœ
Ruby çš‹ćŒèȘžèš€ć…„é–€ć°ŽèŠœRuby çš‹ćŒèȘžèš€ć…„é–€ć°ŽèŠœ
Ruby çš‹ćŒèȘžèš€ć…„é–€ć°ŽèŠœ
 
Dsl
DslDsl
Dsl
 
Language supports it
Language supports itLanguage supports it
Language supports it
 
How To Test Everything
How To Test EverythingHow To Test Everything
How To Test Everything
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Rails
 
Ruby for C# Developers
Ruby for C# DevelopersRuby for C# Developers
Ruby for C# Developers
 
Postobjektové programovanie v Ruby
Postobjektové programovanie v RubyPostobjektové programovanie v Ruby
Postobjektové programovanie v Ruby
 
Ruby Metaprogramming
Ruby MetaprogrammingRuby Metaprogramming
Ruby Metaprogramming
 
Ruby singleton class
Ruby singleton classRuby singleton class
Ruby singleton class
 
Metaprogramovanie #1
Metaprogramovanie #1Metaprogramovanie #1
Metaprogramovanie #1
 
Selfish presentation - ruby internals
Selfish presentation - ruby internalsSelfish presentation - ruby internals
Selfish presentation - ruby internals
 
Self, Class and Module
Self, Class and ModuleSelf, Class and Module
Self, Class and Module
 
Lightning talk
Lightning talkLightning talk
Lightning talk
 

Mehr von ConFoo

Debugging applications with network security tools
Debugging applications with network security toolsDebugging applications with network security tools
Debugging applications with network security toolsConFoo
 
OWASP Enterprise Security API
OWASP Enterprise Security APIOWASP Enterprise Security API
OWASP Enterprise Security APIConFoo
 
Introduction à la sécurité des WebServices
Introduction à la sécurité des WebServicesIntroduction à la sécurité des WebServices
Introduction à la sécurité des WebServicesConFoo
 
Le bon, la brute et le truand dans les nuages
Le bon, la brute et le truand dans les nuagesLe bon, la brute et le truand dans les nuages
Le bon, la brute et le truand dans les nuagesConFoo
 
The Solar Framework for PHP
The Solar Framework for PHPThe Solar Framework for PHP
The Solar Framework for PHPConFoo
 
DĂ©crire un projet PHP dans des rapports
DĂ©crire un projet PHP dans des rapportsDĂ©crire un projet PHP dans des rapports
DĂ©crire un projet PHP dans des rapportsConFoo
 
Server Administration in Python with Fabric, Cuisine and Watchdog
Server Administration in Python with Fabric, Cuisine and WatchdogServer Administration in Python with Fabric, Cuisine and Watchdog
Server Administration in Python with Fabric, Cuisine and WatchdogConFoo
 
Think Mobile First, Then Enhance
Think Mobile First, Then EnhanceThink Mobile First, Then Enhance
Think Mobile First, Then EnhanceConFoo
 
As-t-on encore besoin d'un framework web ?
As-t-on encore besoin d'un framework web ?As-t-on encore besoin d'un framework web ?
As-t-on encore besoin d'un framework web ?ConFoo
 
Pragmatic Guide to Git
Pragmatic Guide to GitPragmatic Guide to Git
Pragmatic Guide to GitConFoo
 
Building servers with Node.js
Building servers with Node.jsBuilding servers with Node.js
Building servers with Node.jsConFoo
 
An Overview of Flash Storage for Databases
An Overview of Flash Storage for DatabasesAn Overview of Flash Storage for Databases
An Overview of Flash Storage for DatabasesConFoo
 
Android Jump Start
Android Jump StartAndroid Jump Start
Android Jump StartConFoo
 
Develop mobile applications with Flex
Develop mobile applications with FlexDevelop mobile applications with Flex
Develop mobile applications with FlexConFoo
 
WordPress pour le développement d'aplications web
WordPress pour le développement d'aplications webWordPress pour le développement d'aplications web
WordPress pour le développement d'aplications webConFoo
 
Graphs, Edges & Nodes: Untangling the Social Web
Graphs, Edges & Nodes: Untangling the Social WebGraphs, Edges & Nodes: Untangling the Social Web
Graphs, Edges & Nodes: Untangling the Social WebConFoo
 
Rendre son CMS conforme au SGQRI 008 en 20 Ă©tapes
Rendre son CMS conforme au SGQRI 008 en 20 Ă©tapesRendre son CMS conforme au SGQRI 008 en 20 Ă©tapes
Rendre son CMS conforme au SGQRI 008 en 20 Ă©tapesConFoo
 

Mehr von ConFoo (17)

Debugging applications with network security tools
Debugging applications with network security toolsDebugging applications with network security tools
Debugging applications with network security tools
 
OWASP Enterprise Security API
OWASP Enterprise Security APIOWASP Enterprise Security API
OWASP Enterprise Security API
 
Introduction à la sécurité des WebServices
Introduction à la sécurité des WebServicesIntroduction à la sécurité des WebServices
Introduction à la sécurité des WebServices
 
Le bon, la brute et le truand dans les nuages
Le bon, la brute et le truand dans les nuagesLe bon, la brute et le truand dans les nuages
Le bon, la brute et le truand dans les nuages
 
The Solar Framework for PHP
The Solar Framework for PHPThe Solar Framework for PHP
The Solar Framework for PHP
 
DĂ©crire un projet PHP dans des rapports
DĂ©crire un projet PHP dans des rapportsDĂ©crire un projet PHP dans des rapports
DĂ©crire un projet PHP dans des rapports
 
Server Administration in Python with Fabric, Cuisine and Watchdog
Server Administration in Python with Fabric, Cuisine and WatchdogServer Administration in Python with Fabric, Cuisine and Watchdog
Server Administration in Python with Fabric, Cuisine and Watchdog
 
Think Mobile First, Then Enhance
Think Mobile First, Then EnhanceThink Mobile First, Then Enhance
Think Mobile First, Then Enhance
 
As-t-on encore besoin d'un framework web ?
As-t-on encore besoin d'un framework web ?As-t-on encore besoin d'un framework web ?
As-t-on encore besoin d'un framework web ?
 
Pragmatic Guide to Git
Pragmatic Guide to GitPragmatic Guide to Git
Pragmatic Guide to Git
 
Building servers with Node.js
Building servers with Node.jsBuilding servers with Node.js
Building servers with Node.js
 
An Overview of Flash Storage for Databases
An Overview of Flash Storage for DatabasesAn Overview of Flash Storage for Databases
An Overview of Flash Storage for Databases
 
Android Jump Start
Android Jump StartAndroid Jump Start
Android Jump Start
 
Develop mobile applications with Flex
Develop mobile applications with FlexDevelop mobile applications with Flex
Develop mobile applications with Flex
 
WordPress pour le développement d'aplications web
WordPress pour le développement d'aplications webWordPress pour le développement d'aplications web
WordPress pour le développement d'aplications web
 
Graphs, Edges & Nodes: Untangling the Social Web
Graphs, Edges & Nodes: Untangling the Social WebGraphs, Edges & Nodes: Untangling the Social Web
Graphs, Edges & Nodes: Untangling the Social Web
 
Rendre son CMS conforme au SGQRI 008 en 20 Ă©tapes
Rendre son CMS conforme au SGQRI 008 en 20 Ă©tapesRendre son CMS conforme au SGQRI 008 en 20 Ă©tapes
Rendre son CMS conforme au SGQRI 008 en 20 Ă©tapes
 

KĂŒrzlich hochgeladen

Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEarley Information Science
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUK Journal
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking MenDelhi Call girls
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?Antenna Manufacturer Coco
 

KĂŒrzlich hochgeladen (20)

Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men08448380779 Call Girls In Civil Lines Women Seeking Men
08448380779 Call Girls In Civil Lines Women Seeking Men
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 

Metaprogramming in Ruby

  • 1. Metaprogramming in Ruby
  • 2. Things I Wish I Knew When I Started Ruby
  • 3.
  • 4.
  • 7. What? Writing programs that write programs.
  • 8. What? Writing programs that write programs. NOT code generation!
  • 10. Why? We all do it. Ruby attr_accessor :my_attribute
  • 11. 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
  • 12. Why? We all do it. Ruby attr_accessor :my_attribute Java public int getAttribute() { return attribute; } public void setAttribute(int newAttribute) { attribute = newAttribute; }
  • 13. Why? We all do it. ner in Ruby W attr_accessor :my_attribute Java public int getAttribute() { return attribute; } public void setAttribute(int newAttribute) { attribute = newAttribute; }
  • 14. Why? def age @age || 'not set' end def gender @gender || 'not set' end def name @name || 'not set' end
  • 15. 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' end end
  • 17. Drawbacks You can write some difïŹcult-to-understand code.
  • 18. Drawbacks You can write some difïŹcult-to-understand code.
  • 20. Method dispatch class MyObject attr_accessor :name def say_hello puts "hello" puts name end end
  • 21. 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?
  • 22. Method dispatch class MyObject attr_accessor :name def say_hello puts "hello" puts self.name end end
  • 23. Method dispatch class MyObject attr_accessor :name def say_hello puts "hello" puts self.name end end self is always the implied receiver!
  • 24. Method dispatch class MyObject attr_accessor :name def say_hello puts "hello" puts name end end
  • 25. Method dispatch class MyObject self.attr_accessor :name def say_hello puts "hello" puts name end end
  • 26. Method dispatch class MyObject Who is self self.attr_accessor :name here? def say_hello puts "hello" puts name end end
  • 27. Method dispatch class MyObject Who is self self.attr_accessor :name here? def say_hello puts "hello" The class puts name end is! end
  • 28. Method dispatch Module#attr Who is self here? The class is!
  • 30. Ruby classes class NewClass def hey puts 'hello!' end end
  • 31. Ruby classes class NewClass { def hey puts 'hello!' end end This is normal code!
  • 32. Ruby classes class NewClass def hey puts 'hello!' end end is the same as NewClass = Class.new do def hey puts 'hello!' end end
  • 33. Ruby classes class ParsingError < RuntimeError end
  • 34. Ruby classes class ParsingError < RuntimeError end is the same as ParsingError = Class.new(RuntimeError)
  • 35. Ruby classes def class_with_accessors(*attributes) Class.new do attr_accessor *attributes end end
  • 36. Ruby classes def class_with_accessors(*attributes) Class.new do attr_accessor *attributes end end Returns a new class!
  • 37. Ruby classes def class_with_accessors(*attributes) Class.new do attr_accessor *attributes end end Returns a new class! class Person < class_with_accessors(:name, :age, :sex) # ... end
  • 38. eval, instance_eval, class_eval
  • 39. eval, instance_eval, class_eval Method eval instance_eval class_eval
  • 40. eval, instance_eval, class_eval Method Context eval your current context instance_eval the object class_eval the object’s class
  • 41. eval, instance_eval, class_eval eval eval "puts 'hello'" # hello
  • 42. eval, instance_eval, class_eval instance_eval class MyClass end MyClass.instance_eval("def hi; 'hi'; end")
  • 43. eval, instance_eval, class_eval instance_eval class MyClass end MyClass.instance_eval("def hi; 'hi'; end") MyClass.hi # 'hi'
  • 44. eval, instance_eval, class_eval instance_eval class MyClass end MyClass.instance_eval("def hi; 'hi' end") obj = MyClass.new # <MyClass:0x10178aff8> obj.hi # NoMethodError: undefined method `hi' for #<MyClass>
  • 45. eval, instance_eval, class_eval class_eval class MyClass end MyClass.class_eval("def hi; 'hi' end")
  • 46. eval, instance_eval, class_eval class_eval class MyClass end MyClass.class_eval("def hi; 'hi' end") MyClass.hi # NoMethodError: undefined method `hi' for MyClass:Class
  • 47. eval, instance_eval, class_eval class_eval class MyClass end MyClass.class_eval("def hi; 'hi' end") obj = MyClass.new # <MyClass:0x101849688> obj.hi # "hi"
  • 48. eval, instance_eval, class_eval class_eval instance_eval class MyClass class MyClass end end MyClass.class_eval("def hi; 'hi'; end") MyClass.instance_eval("def hi; 'hi'; end") obj = MyClass.new MyClass.hi # <MyClass:0x101849688> # 'hi' obj.hi # "hi"
  • 49. eval, instance_eval, class_eval Ninja’s will attack you if ... you don’t use __FILE__, __LINE__
  • 50. eval, instance_eval, class_eval class HiThere end HiThere.class_eval "def hi; raise; end" HiThere.class_eval "def hi_with_niceness; raise; end", __FILE__, __LINE__ HiThere.new.hi HiThere.new.hi_with_niceness
  • 51. eval, instance_eval, class_eval class HiThere end HiThere.class_eval "def hi; raise; end" HiThere.class_eval "def hi_with_niceness; raise; end", __FILE__, __LINE__ HiThere.new.hi HiThere.new.hi_with_niceness (eval):1:in `hi': unhandled exception from my_file.rb:7 my_file.rb:5:in `hi_with_niceness': unhandled exception from my_file.rb:7
  • 52. eval, instance_eval, class_eval class HiThere end HiThere.class_eval "def hi; raise; end" HiThere.class_eval "def hi_with_niceness; raise; end", __FILE__, __LINE__ HiThere.new.hi HiThere.new.hi_with_niceness (eval):1:in `hi': unhandled exception from my_file.rb:7 So nice. <3 my_file.rb:5:in `hi_with_niceness': unhandled exception from my_file.rb:7
  • 53. eval, instance_eval, class_eval Implement attr_accessor!
  • 54. eval, instance_eval, class_eval class Module def create_attr(attribute) class_eval("def #{attribute}; @#{attribute}; end") end end
  • 55. eval, instance_eval, class_eval class Module def create_attr(attribute) class_eval("def #{attribute}; @#{attribute}; end") end end class M create_attr :hi end
  • 56. eval, instance_eval, class_eval class Module def create_attr(attribute) class_eval("def #{attribute}; @#{attribute}; end") end end def M class M def hi create_attr :hi @hi end end end
  • 58. DeïŹning methods For an object o = Object.new o.instance_eval("def just_this_object; end") o.just_this_object
  • 59. DeïŹning methods For an object o = Object.new o.instance_eval("def just_this_object; end") o.just_this_object Object.new.just_this_object # NoMethodError: undefined method `just_this_object'
  • 60. DeïŹning methods For an object o = Object.new o.instance_eval("def just_this_object; end") o.just_this_object Object.new.just_this_object # NoMethodError: undefined method `just_this_object' o = Object.new o.instance_eval { def just_this_object end }
  • 61. DeïŹning methods For an object o = Object.new o.extend(Module.new { def just_this_object end })
  • 62. DeïŹning methods For a class MyClass = Class.new class MyClass def new_method end end MyClass.new.respond_to?(:new_method) # true
  • 63. DeïŹning methods For a class MyClass = Class.new MyClass.class_eval " MyClass.class_eval do def new_method def new_method end end " end MyClass.send(:define_method, :new_method) { # your method body }
  • 64. DeïŹning methods Aliasing Module#alias_method
  • 66. Scoping module Project class Main def run # ... end end end
  • 67. Scoping module Project class Main def run # ... end end end Class deïŹnitions Module deïŹnitions Method deïŹnitions
  • 68. Scoping a = 'hello' module Project class Main def run puts a end end end Project::Main.new.run
  • 69. 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)
  • 70. Scoping module Project class Main end end a = 'hello' Project::Main.class_eval do define_method(:run) do puts a end end Project::Main.new.run # => hello
  • 71. Scoping Example: Connection Sharing module 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 end end
  • 72. Scoping Example: Connection Sharing module 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 end end Client = Class.new AddConnections.add_connection_methods(Client, 'localhost', 8080) Client.new.get_connection # Getting connection for localhost:8080 Client.new.host # localhost Client.new.port # 8080
  • 73. Scoping Kernel#binding Let’s you leak the current “bindings”
  • 74. Scoping Kernel#binding Let’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
  • 75. Scoping Kernel#binding Let’s you leak the current “bindings” def create_connection(bind) eval ' connection = "I am a connection" ', bind end Calls connection = nil with the create_connection(binding) current connection # => I am a connection state
  • 76. Scoping Kernel#binding Let’s you leak the current “bindings” def create_connection(bind) eval ' connection = "I am a connection" ', bind end Calls connection = nil with the create_connection(binding) current connection # => I am a connection state MAGIC!
  • 77. Scoping Kernel#binding Let’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'
  • 78. Scoping Kernel#binding Let’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
  • 79. Scoping Kernel#binding Let’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
  • 81. Scoping Kernel#binding TOPLEVEL_BINDING a = 'hello' module Program class Main def run puts eval("a", TOPLEVEL_BINDING) end end end Program::Main.new.run # => hello
  • 84. Interception! method_missing(method, *args, &blk) class MethodMissing def method_missing(m, *args, &blk) puts "method_missing #{m} #{args.inspect} #{blk.inspect}" super end end
  • 85. Interception! method_missing(method, *args, &blk) class MethodMissing def method_missing(m, *args, &blk) puts "method_missing #{m} #{args.inspect} #{blk.inspect}" super end end mm = MethodMissing.new mm.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>
  • 86. 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
  • 87. 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.new class 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
  • 88. Interception! Example: Proxy class Proxy def initialize(backing) @backing = backing end def method_missing(m, *args, &blk) @backing.send(m, *args, &blk) end end
  • 89. Interception! Example: Proxy class 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) end end
  • 90. 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
  • 92. Interception! Object#respond_to?(sym) Example: Timing module 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) end end
  • 93. Interception! Object#respond_to?(sym) Example: Timing module 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) end end
  • 94. Interception! Object#respond_to_missing?(sym) (1.9 only) 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 def respond_to_missing?(sym) (timed_method = sym.to_s[/^(.*)_with_timing$/, 1]) ? respond_to?(timed_method.to_sym) : super end end
  • 97. Interception! const_missing(sym) MyClass::MyOtherClass # MyClass.const_missing(:MyOtherClass) Example: Loader class 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 "can't find #{file}, sorry!" super end end end
  • 98. Interception! Example: Loader class 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 "can't find #{file}, sorry!" super end end end
  • 99. Interception! Example: Loader class 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 "can't find #{file}, sorry!" super end end end Loader::Auto # can't find ./auto.rb, sorry! # NameError: uninitialized constant Loader::Auto # or, if you have an ./auto.rb Loader::Auto # => Auto
  • 103. Callbacks Module#method_added class 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
  • 104. Callbacks Module#method_added class 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
  • 105. Callbacks Module#method_added Example: 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 isn't documented" : "This action doesn't exist" end desc "Start server" def start end def stop end end
  • 106. Callbacks Module#method_added Example: Thor! Record the description class 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 isn't documented" : "This action doesn't exist" end desc "Start server" def start end def stop end end
  • 107. Callbacks Module#method_added Example: Thor! Record the description class 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 isn't documented" : "This action doesn't exist" end desc "Start server" def start end Described! def stop end end
  • 108. Callbacks Module#method_added Example: 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 isn't documented def self.method_added(m) puts Tasks.method_description(:restart) (@method_descs ||= {})[m] = @desc # => This action doesn't exist @desc = nil end def self.method_description(m) method_defined?(m) ? @method_descs[m] || "This action isn't documented" : "This action doesn't exist" end desc "Start server" def start end def stop end end
  • 110. Callbacks Object#singleton_method_added class ClassWithMethods def self.singleton_method_added(m) puts "ADDING! #{m}" end def self.another end end
  • 111. Callbacks Object#singleton_method_added class ClassWithMethods def self.singleton_method_added(m) puts "ADDING! #{m}" end def self.another end end # ADDING! singleton_method_added # ADDING! another
  • 112. Callbacks Object#singleton_method_added class ClassWithMethods def self.singleton_method_added(m) puts "ADDING! #{m}" end def self.another end end Holy meta! # ADDING! singleton_method_added # ADDING! another
  • 113. Callbacks Module#included module Logger def self.included(m) puts "adding logging to #{m}" end end class Server include Logger end # adding logging to Server
  • 114. Callbacks Module#included Example: ClassMethods pattern module 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 end end Server.create # `create': undefined method `log' for Server:Class (NoMethodError)
  • 115. Callbacks Module#included Example: ClassMethods pattern module 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 end end Server.create # LOG: Creating server!
  • 116. Callbacks Module#extended module One def self.extended(obj) puts "#{self} has been extended by #{obj}" end end Object.new.extend(One) # One has been extended by #<Object:0x1019614a8>
  • 117. Callbacks Class#inherited class Parent def self.inherited(o) puts "#{self} was inherited by #{o}" end end class Child < Parent end # Parent was inherited by Child
  • 118. Callbacks Guarding callbacks Module#append_features include Module#extend_object extend def self.extend_object(o) super end def self.append_features(o) super end
  • 119. Callbacks Guarding callbacks Module#append_features include Module#extend_object extend def self.append_features(o) o.instance_method(:<=>) ? super : warn('you no can uze') end
  • 120. Callbacks Kernel#caller def one two end def two method name three ïŹle name line (optional) end def three p caller end # ["method.rb:156:in `two'", "method.rb:152:in `one'", "method.rb:163"] https://github.com/joshbuddy/callsite
  • 121. Callbacks Module#nesting module A module B module C p Module.nesting end end end # [A::B::C, A::B, A]
  • 122. There and back again, a parsing tale
  • 123. There and back again, a parsing tale gem install ruby_parser gem install sexp_processor gem install ruby2ruby Let’s go!
  • 124. There and back again, a parsing tale Parsing require 'rubygems' require 'ruby_parser' RubyParser.new.process("'string'") s(:str, "string") Type Arguments...
  • 125. There and back again, a parsing tale Parsing require 'rubygems' require 'ruby_parser' RubyParser.new.process("'string'") s(:str, "string") [:str, "string"] # Sexp Sexp.superclass # Array
  • 126. There and back again, a parsing tale Parsing RubyParser.new.process("'string' + 'string'") s(:call, s(:str, "string"), :+, s(:arglist, s(:str, "string"))) Method Method Receiver Arguments call name
  • 127. There and back again, a parsing tale Parsing RubyParser.new.process("'string' + 'string'") s(:call, nil, :puts, s(:arglist, s(:str, "hello world"))) Method Receiver Method Arguments call name
  • 128. There and back again, a parsing tale And, back again... require 'rubygems' require 'ruby2ruby' Ruby2Ruby.new.process [:str, "hello"] # => "hello"
  • 129. There and back again, a parsing tale And, back again... require 'rubygems' require 'ruby2ruby' Ruby2Ruby.new.process [:str, "hello"] # => "hello" Ruby2Ruby.new.process [:lit, :symbol] # => :symbol
  • 130. There and back again, a parsing tale Roundtrip 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
  • 131. There and back again, a parsing tale Roundtrip 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")
  • 132. There and back again, a parsing tale Roundtrip 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")