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_attribute
Java public int getAttribute() {
return attribute;
}
public void setAttribute(int newAttribute) {
attribute = newAttribute;
}
11.
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;
}
12.
Why?
def age
@age || 'not set'
end
def gender
@gender || 'not set'
end
def name
@name || 'not set'
end
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'
end
end
28.
Ruby classes
class NewClass
def hey
puts 'hello!'
end
end
29.
Ruby classes
class NewClass
{
def hey
puts 'hello!'
end
end
This is normal
code!
30.
Ruby classes
class NewClass
def hey
puts 'hello!'
end
end
is the same as
NewClass = Class.new do
def hey
puts 'hello!'
end
end
31.
Ruby classes
class ParsingError < RuntimeError
end
32.
Ruby classes
class ParsingError < RuntimeError
end
is the same as
ParsingError = Class.new(RuntimeError)
33.
Ruby classes
def class_with_accessors(*attributes)
Class.new do
attr_accessor *attributes
end
end
34.
Ruby classes
def class_with_accessors(*attributes)
Class.new do
attr_accessor *attributes
end
end
Returns a new class!
35.
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
52.
eval, instance_eval,
class_eval
class Module
def create_attr(attribute)
class_eval("def #{attribute}; @#{attribute}; end")
end
end
53.
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
54.
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
56.
Defining methods
For an object
o = Object.new
o.instance_eval("def just_this_object; end")
o.just_this_object
57.
Defining 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'
58.
Defining 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
}
59.
Defining methods
For an object
o = Object.new
o.extend(Module.new {
def just_this_object
end
})
60.
Defining methods
For a class
MyClass = Class.new
class MyClass
def new_method
end
end
MyClass.new.respond_to?(:new_method) # true
61.
Defining 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.
Scoping
module Project
class Main
def run
# ...
end
end
end
65.
Scoping
module Project
class Main
def run
# ...
end
end
end
Class definitions
Module definitions
Method definitions
66.
Scoping
a = 'hello'
module Project
class Main
def run
puts a
end
end
end
Project::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
end
Project::Main.new.run # => hello
69.
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
70.
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
71.
Scoping
Kernel#binding
Let’s you leak the current “bindings”
72.
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
73.
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
74.
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!
75.
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'
76.
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
77.
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
79.
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
82.
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
83.
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>
84.
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
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.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
86.
Interception!
Example: Proxy
class Proxy
def initialize(backing)
@backing = backing
end
def method_missing(m, *args, &blk)
@backing.send(m, *args, &blk)
end
end
87.
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
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
90.
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
91.
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
92.
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
95.
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
96.
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
97.
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
101.
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
102.
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
103.
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
104.
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
105.
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
106.
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
108.
Callbacks
Object#singleton_method_added
class ClassWithMethods
def self.singleton_method_added(m)
puts "ADDING! #{m}"
end
def self.another
end
end
109.
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
110.
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
111.
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
112.
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)
113.
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!
114.
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>
115.
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
116.
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
117.
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
118.
Callbacks
Kernel#caller
def one
two
end
def two
method name
three
file 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
119.
Callbacks
Module#nesting
module A
module B
module C
p Module.nesting
end
end
end
# [A::B::C, A::B, A]
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 tale
Parsing
require 'rubygems'
require 'ruby_parser'
RubyParser.new.process("'string'")
s(:str, "string")
Type Arguments...
123.
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
124.
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
125.
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
126.
There and back again, a
parsing tale
And, back again...
require 'rubygems'
require 'ruby2ruby'
Ruby2Ruby.new.process [:str, "hello"] # => "hello"
127.
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
128.
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
129.
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")
130.
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")
Il semblerait que vous ayez déjà ajouté cette diapositive à .
Créer un clipboard
Vous avez clippé votre première diapositive !
En clippant ainsi les diapos qui vous intéressent, vous pourrez les revoir plus tard. Personnalisez le nom d’un clipboard pour mettre de côté vos diapositives.
Créer un clipboard
Partager ce SlideShare
Vous avez les pubs en horreur?
Obtenez SlideShare sans publicité
Bénéficiez d'un accès à des millions de présentations, documents, e-books, de livres audio, de magazines et bien plus encore, sans la moindre publicité.
Offre spéciale pour les lecteurs de SlideShare
Juste pour vous: Essai GRATUIT de 60 jours dans la plus grande bibliothèque numérique du monde.
La famille SlideShare vient de s'agrandir. Profitez de l'accès à des millions de livres numériques, livres audio, magazines et bien plus encore sur Scribd.
Apparemment, vous utilisez un bloqueur de publicités qui est en cours d'exécution. En ajoutant SlideShare à la liste blanche de votre bloqueur de publicités, vous soutenez notre communauté de créateurs de contenu.
Vous détestez les publicités?
Nous avons mis à jour notre politique de confidentialité.
Nous avons mis à jour notre politique de confidentialité pour nous conformer à l'évolution des réglementations mondiales en matière de confidentialité et pour vous informer de la manière dont nous utilisons vos données de façon limitée.
Vous pouvez consulter les détails ci-dessous. En cliquant sur Accepter, vous acceptez la politique de confidentialité mise à jour.