Permalink
Browse files

* method_missing - works much better now

 * added example from Hirschfeld paper
 * added autotest folder - now runs specs and tests in parallel
 * literate_maruku_test is now 1.9 aware


git-svn-id: svn+ssh://rubyforge.org/var/svn/contextr/trunk@86 b232672f-569e-4225-9534-19f781cde581
  • Loading branch information...
schmidt
schmidt committed Jan 8, 2008
1 parent c8ce852 commit 6ef36a275f2ff13e8f67d48558704bedaf6a9f0e
View
@@ -0,0 +1,3 @@
Autotest.add_discovery do
["rspec", "test"]
end
View
@@ -0,0 +1,13 @@
require 'rubygems'
require 'autotest/rspec'
class Autotest
class RspecTest
def self.run
Thread.new do
Autotest::Rspec.run
end
Autotest.run
end
end
end
View
@@ -0,0 +1,213 @@
require "rubygems"
require "contextr"
module Cop
# Fig. 3
class Node
attr_accessor :first, :op, :second
def initialize(values = {})
values.each do |key, value|
self.send("#{key}=", value)
end
end
end
class Leaf
attr_accessor :value
def initialize(value)
self.value = value
end
end
# Fig. 4
class ExampleTree
def initialize
@tree = Node.new(
:first => Node.new(
:first => Node.new(
:first => Leaf.new(1),
:op => :+,
:second => Leaf.new(2)),
:op => :*,
:second => Node.new(
:first => Leaf.new(3),
:op => :-,
:second => Leaf.new(4))),
:op => :/,
:second => Leaf.new(5))
end
end
# Fig. 5
class Node
def evaluate
# first.evaluate.send(op, second.evaluate.to_f)
first.evaluate.send(op, second.evaluate)
end
def print_prefix
"(#{op}#{first.print_prefix}#{second.print_prefix})"
end
def print_infix
"(#{first.print_infix}#{op}#{second.print_infix})"
end
def print_postfix
"(#{first.print_postfix}#{second.print_postfix}#{op})"
end
end
class Leaf
def evaluate
value
end
def print_infix
value.to_s
end
def print_postfix
value.to_s
end
def print_prefix
value.to_s
end
end
# Fig. 7
class ExampleTree
def show
puts @tree.print_infix
puts @tree.print_prefix
puts @tree.print_postfix
puts @tree.evaluate
end
end
ExampleTree.new.show
# Fig. 8
class Node
def accept(visitor)
visitor.visit(self)
end
end
class Leaf
def accept(visitor)
visitor.visit(self)
end
end
# Fig. 9
class Visitor
def visit(node_or_leaf)
case node_or_leaf
when Node
visit_node(node_or_leaf)
when Leaf
visit_leaf(node_or_leaf)
end
end
end
class PrintPrefixVisitor < Visitor
def visit_leaf(leaf)
leaf.value.to_s
end
def visit_node(node)
"(#{node.op}#{node.first.accept(self)}#{node.second.accept(self)})"
end
end
class PrintInfixVisitor < Visitor
def visit_leaf(leaf)
leaf.value.to_s
end
def visit_node(node)
"(#{node.first.accept(self)}#{node.op}#{node.second.accept(self)})"
end
end
class PrintPostfixVisitor < Visitor
def visit_leaf(leaf)
leaf.value.to_s
end
def visit_node(node)
"(#{node.first.accept(self)}#{node.second.accept(self)}#{node.op})"
end
end
class EvaluateVisitor < Visitor
def visit_leaf(leaf)
leaf.value
end
def visit_node(node)
# node.first.accept(self).send(node.op, node.second.accept(self).to_f)
node.first.accept(self).send(node.op, node.second.accept(self))
end
end
# Fig. 10
class ExampleTree
def show_visitor
puts @tree.accept(PrintInfixVisitor.new)
puts @tree.accept(PrintPrefixVisitor.new)
puts @tree.accept(PrintPostfixVisitor.new)
puts @tree.accept(EvaluateVisitor.new)
end
end
ExampleTree.new.show_visitor
# Fig. 11
class Leaf
in_layer :print_prefix do
def to_s
yield(:receiver).value.to_s
end
end
in_layer :print_infix do
def to_s
yield(:receiver).value.to_s
end
end
in_layer :print_postfix do
def to_s
yield(:receiver).value.to_s
end
end
in_layer :evaluate do
def evaluate
yield(:receiver).value
end
end
end
class Node
in_layer :print_prefix do
def to_s
node = yield(:receiver)
"(#{node.op}#{node.first}#{node.second})"
end
end
in_layer :print_infix do
def to_s
node = yield(:receiver)
"(#{node.first}#{node.op}#{node.second})"
end
end
in_layer :print_postfix do
def to_s
node = yield(:receiver)
"(#{node.first}#{node.second}#{node.op})"
end
end
in_layer :evaluate do
def evaluate
node = yield(:receiver)
# node.first.evaluate.send(node.op, node.second.evaluate.to_f)
node.first.evaluate.send(node.op, node.second.evaluate)
end
end
end
# Fig. 12
class ExampleTree
def show_layers
ContextR::with_layer(:print_infix) { puts @tree }
ContextR::with_layer(:print_prefix) { puts @tree }
ContextR::with_layer(:print_postfix) { puts @tree }
ContextR::with_layer(:evaluate) { puts @tree.evaluate }
end
end
ExampleTree.new.show_layers
end
@@ -40,7 +40,7 @@ def call_methods_stack(stack, receiver, method_name, arguments, block)
if stack.size == 1
stack.pop.call(*arguments, &block)
else
stack.pop.__send__(method_name, *arguments) do | action, *rest_args |
stack.pop.__send__(method_name, *arguments) do |action, *rest_args|
case action
when :receiver
receiver
@@ -51,6 +51,7 @@ def call_methods_stack(stack, receiver, method_name, arguments, block)
when :block_given?
!block.nil?
when :next
rest_args.shift if method_name != :method_missing
call_methods_stack(stack, receiver, method_name, rest_args, block)
else
raise ArgumentError, "Use only :receiver, :block, :block_given?, " +
@@ -40,7 +40,7 @@ def reveal(name)
instance_methods.each { |m| hide(m) }
def method_missing(method_name, *rest_args)
def method_missing(*rest_args)
if block_given?
yield(:next, *rest_args)
else
View
@@ -101,7 +101,7 @@ Now let's compute Fib(100) again. Of course with caching enabled
example do
timeout_raised = false
begin
Timeout::timeout(0.05) do
Timeout::timeout(0.1) do
ContextR::with_layer :cache do
result_of(Fibonacci.compute(100)) == 354_224_848_179_261_915_075
end
@@ -9,10 +9,10 @@ def ordinal
return 'th' if (10..19).include?(self % 100)
# others
case self % 10
when 1: return 'st'
when 2: return 'nd'
when 3: return 'rd'
else return 'th'
when 1 then return 'st'
when 2 then return 'nd'
when 3 then return 'rd'
else return 'th'
end
end
end
View
@@ -0,0 +1,40 @@
It is used in ContextR as well, and its
redefinition is simple, The functionality of method missing is
one of the core ingredients of cleanly designed Ruby programs, it is still
possible to extend it with context-dependent behaviour.
The following code will show the right usage.
class MethodMissingExample
def method_missing(*a)
"base"
end
in_layer :one do
def method_missing(*a, &b)
"pre_one " + super
end
end
in_layer :two do
def method_missing(*a, &b)
super + " post_two"
end
end
end
example do
instance = MethodMissingExample.new
result_of(instance.any_method) == "base"
ContextR::with_layer :one do
result_of(instance.any_method) == "pre_one base"
end
ContextR::with_layer :two do
result_of(instance.any_method) == "base post_two"
end
ContextR::with_layer :one, :two do
result_of(instance.any_method) == "pre_one base post_two"
end
end
View
@@ -4,7 +4,7 @@ that are used internally in ContextR. Extending them would simply result in
endless stacks.
In custom classes, i.e. classes defined by your code, every method may be
extended. There are only 4 exceptions and one corner case.
extended. There are only 4 exceptions.
First the exceptions. You may never extend
@@ -13,54 +13,9 @@ First the exceptions. You may never extend
* `__clone__`
* `extend`
The first shall not be redefined in any case. That is why this triggers a
The first shall not be redefined anyway. That is why it triggers a
warning. The latter is used by ContextR. This currently cannot be circumvented.
`method_missing`
----------------
`method_missing` is the corner case. It is used in ContextR as well, and its
redefinition is difficult, but since the functionality of method missing is
one of the core ingredients of cleanly designed Ruby programs, it is still
possible to extend it with context-dependent behaviour. There is one simple
limitation. Do not use `super` to call the next layer, but use `yield(:next)`
instead.
The following code will show the right usage.
class MethodMissingExample
def method_missing(*a)
"base"
end
in_layer :one do
def method_missing(*a)
"pre_one " + yield(:next, *a)
end
end
in_layer :two do
def method_missing(*a)
yield(:next, *a) + " post_two"
end
end
end
example do
instance = MethodMissingExample.new
result_of(instance.any_method) == "base"
ContextR::with_layer :one do
result_of(instance.any_method) == "pre_one base"
end
ContextR::with_layer :two do
result_of(instance.any_method) == "base post_two"
end
ContextR::with_layer :one, :two do
result_of(instance.any_method) == "pre_one base post_two"
end
end
Accessing `self`
----------------
View
@@ -8,7 +8,7 @@
require File.dirname(__FILE__) + "/test_helper.rb"
%w(class_side dynamic_scope dynamics hello_world introduction
layer_state meta_api ordering restrictions).each do |test|
layer_state meta_api method_missing ordering restrictions).each do |test|
test_class("test_#{test}".camelcase.to_sym)
LiterateMarukuTest.load(test)
end

0 comments on commit 6ef36a2

Please sign in to comment.