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...
1 parent c8ce852 commit 6ef36a275f2ff13e8f67d48558704bedaf6a9f0e schmidt committed Jan 8, 2008
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.