Skip to content

Instantly share code, notes, and snippets.

@kinopyo
Created September 6, 2011 01:22
Show Gist options
  • Save kinopyo/1196318 to your computer and use it in GitHub Desktop.
Save kinopyo/1196318 to your computer and use it in GitHub Desktop.
Here're code snippets from the book: Metaprogramming in Ruby.
#---
# Excerpted from "Metaprogramming Ruby",
# published by The Pragmatic Bookshelf.
# Copyrights apply to this code. It may not be used to create training material,
# courses, books, articles, and the like. Contact us if you are in doubt.
# We make no guarantees that this code is fit for any purpose.
# Visit http://www.pragmaticprogrammer.com/titles/ppmetr for more book information.
#---
# the benchmark shows that ghost_reverse( ) is about twice as slow as reverse( )
# You might be able to arrange things so that
# the first call to a Ghost Method defines a Dynamic Method (68) for the next calls.
# You’ll see an example of this technique and a discussion of its trade-offs in Chapter 8,
# Inside ActiveRecord, on page 206.
class String
def method_missing(method, *args)
method == :ghost_reverse ? reverse : super
end
end
require 'benchmark'
Benchmark.bm do |b|
b.report 'Normal method' do
1000000.times { "abc".reverse }
end
b.report 'Ghost method ' do
1000000.times { "abc".ghost_reverse }
end
end
#---
# Excerpted from "Metaprogramming Ruby",
# published by The Pragmatic Bookshelf.
# Copyrights apply to this code. It may not be used to create training material,
# courses, books, articles, and the like. Contact us if you are in doubt.
# We make no guarantees that this code is fit for any purpose.
# Visit http://www.pragmaticprogrammer.com/titles/ppmetr for more book information.
#---
# Computer with define_method() and HWDataSource introspection
require 'data_source'
class Computer
def initialize(computer_id, data_source)
@id = computer_id
@data_source = data_source
data_source.methods.grep(/^get_(.*)_info$/) { Computer.define_component $1 }
end
def self.define_component(name)
# Module#define_method() define a method on the spot
# This technique of defining a method at runtime is called a Dynamic Method
#
# Object#send(), call methods dynamically
# This technique is called Dynamic Dispatch
define_method(name) do
info = @data_source.send "get_#{name}_info", @id
price = @data_source.send "get_#{name}_price", @id
result = "#{name.capitalize}: #{info} ($#{price})"
return "* #{result}" if price >= 100
result
end
end
end
#---
# Excerpted from "Metaprogramming Ruby",
# published by The Pragmatic Bookshelf.
# Copyrights apply to this code. It may not be used to create training material,
# courses, books, articles, and the like. Contact us if you are in doubt.
# We make no guarantees that this code is fit for any purpose.
# Visit http://www.pragmaticprogrammer.com/titles/ppmetr for more book information.
#---
# Computer as a Blank Slate (a better one than in blank.rb)
class Computer
# make it a BlankSlate - a class that has fewer methods than the Object class itself.
# Module#undef_ method( ) removes all methods, including the inherited ones.
# Module#remove_method() removes the method from the receiver, but it leaves inherited methods alone.
# Ruby has two such reserved methods, __send__() and __id__(), which are synonyms for send() and id( ).
# Here is what I add: object_id, since I got warning below:
# warning: undefining `object_id' may cause serious problems
# I run ruby 1.9.2-p290
instance_methods.each do |m|
undef_method m unless m.to_s =~ /^__|method_missing|respond_to?|object_id/
end
# ...
def initialize(computer_id, data_source)
@id = computer_id
@data_source = data_source
end
def method_missing(name, *args)
super if !respond_to?(name)
info = @data_source.send("get_#{name}_info", args[0])
price = @data_source.send("get_#{name}_price", args[0])
result = "#{name.to_s.capitalize}: #{info} ($#{price})"
return "* #{result}" if price >= 100
result
end
def respond_to?(method)
@data_source.respond_to?("get_#{method}_info") || super
end
# ...
end
class Roulette
def method_missing(name, *args)
person = name.to_s.capitalize
# Principle: don’t introduce more Ghost Methods than necessary
# remember to fall back on Kernel#method_missing()
# when you get a call that you don’t know how to deal with
super unless %w[Bob Frank Bill].include? person
# if you don't declare this number variable here
# it will be bugged, since it assuumes that number
# must be a parentheses-less method call on self.
# and will cause an infinite loop
# try to comment out it and see the result.
number = 0
3.times do
number = rand(10) + 1
puts "#{number}..."
end
"#{person} got a #{number}"
end
end
r = Roulette.new
p r.bob
p r.frank
p r.other_guy
#---
# Excerpted from "Metaprogramming Ruby",
# published by The Pragmatic Bookshelf.
# Copyrights apply to this code. It may not be used to create training material,
# courses, books, articles, and the like. Contact us if you are in doubt.
# We make no guarantees that this code is fit for any purpose.
# Visit http://www.pragmaticprogrammer.com/titles/ppmetr for more book information.
#---
def a_method(a, b)
# The block is passed straight into the method,
# and the method can then call back to the block with the yield keyword.
a + yield(a, b)
end
# define a block with either curly braces or the do...end key- words.
# like a method, a block returns the result of the last line of code it evaluates.
p a_method(1, 2) {|x, y| (x + y) * 3 } # => 10
def a_method
# If you use yield when block_given?( ) is false, you’ll get a runtime error.
return yield if block_given?
'no block'
end
p a_method # => "no block"
p a_method { "here's a block!" } # => "here's a block!"
#---
# Excerpted from "Metaprogramming Ruby",
# published by The Pragmatic Bookshelf.
# Copyrights apply to this code. It may not be used to create training material,
# courses, books, articles, and the like. Contact us if you are in doubt.
# We make no guarantees that this code is fit for any purpose.
# Visit http://www.pragmaticprogrammer.com/titles/ppmetr for more book information.
#---
# Kernel#using() takes the managed resource as an argument.
# It also takes a block, which it executes.
module Kernel
def using(resource)
# In case of an exception, also rethrows the exception to the caller.
begin
yield
# Whether or not the block completes normally,
# the ensure clause calls dispose() on the resource to release it cleanly.
ensure
resource.dispose
end
end
end
#---
# Excerpted from "Metaprogramming Ruby",
# published by The Pragmatic Bookshelf.
# Copyrights apply to this code. It may not be used to create training material,
# courses, books, articles, and the like. Contact us if you are in doubt.
# We make no guarantees that this code is fit for any purpose.
# Visit http://www.pragmaticprogrammer.com/titles/ppmetr for more book information.
#---
# You’re probably wondering where the block picks up its bindings.
# When you define the block, it simply grabs the bindings that are there at that moment,
# and then it carries those bindings along when you pass the block into a method:
def my_method
x = "Goodbye"
yield("cruel")
end
x = "Hello"
my_method {|y| "#{x}, #{y} world" } # => "Hello, cruel world"
# A block captures the bindings that are around when you first define the block.
# You can also define additional bindings inside the block,
# but they disappear after the block ends
def my_method2
yield
end
top_level_variable = 1
my_method2 do
top_level_variable += 1
local_to_block = 1
end
top_level_variable # => 2
local_to_block # => Error!
# lists the instance methods of Object that begin with a d:
Object.instance_methods.grep /^d/ # => [:dup, :display, :define_singleton_method]
block_given?
# track the names of bindings
Kernel#local_variables( )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment