Created
September 6, 2011 01:22
-
-
Save kinopyo/1196318 to your computer and use it in GitHub Desktop.
Here're code snippets from the book: Metaprogramming in Ruby.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#--- | |
# 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#--- | |
# 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#--- | |
# 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#--- | |
# 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!" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#--- | |
# 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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#--- | |
# 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! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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