Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial idea, down in code.

  • Loading branch information...
commit 83abfcbd64b5596a9b8df65c1779ea97ea3101a0 0 parents
@mike-burns authored
5 .gitignore
@@ -0,0 +1,5 @@
+*.gem
+.bundle
+Gemfile.lock
+pkg/*
+*swp
4 Gemfile
@@ -0,0 +1,4 @@
+source "http://rubygems.org"
+
+# Specify your gem's dependencies in method_missing.gemspec
+gemspec
30 LICENSE
@@ -0,0 +1,30 @@
+Copyright (c)2011, Mike Burns
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ * Neither the name of Mike Burns nor the names of other
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6 Rakefile
@@ -0,0 +1,6 @@
+require 'bundler/gem_tasks'
+require 'rspec/core/rake_task'
+
+RSpec::Core::RakeTask.new(:spec)
+
+task :default => :spec
2  lib/method_missing.rb
@@ -0,0 +1,2 @@
+require "method_missing/version"
+require 'method_missing/method_extension'
42 lib/method_missing/ap_method.rb
@@ -0,0 +1,42 @@
+require 'method_missing/cons_method'
+
+class ApMethod
+ def initialize(methods)
+ @methods = methods
+ end
+
+ def *(f)
+ ApMethod.new(@methods.map {|m| ConsMethod.new(m, f)})
+ end
+
+ def /(f)
+ ApMethod.new(@methods + [f])
+ end
+
+ def ^(power)
+ ApMethod.new(@methods.map {|m| m ^ power})
+ end
+
+
+ def call(*x)
+ @methods.map{|m| m.call(*x)}
+ end
+
+ def to_proc
+ lambda {|*x|
+ @methods.map {|m| m.call(*x) }
+ }
+ end
+
+ def inspect
+ "#<ApMethod: #{@methods.map{|m| m.inspect}.join(' / ')}>"
+ end
+
+ def arity
+ @methods.first.arity
+ end
+
+ def [](*x)
+ call(*x)
+ end
+end
53 lib/method_missing/cons_method.rb
@@ -0,0 +1,53 @@
+require 'method_missing/ap_method'
+
+class ConsMethod
+ def initialize(f,g)
+ @f = f
+ @g = g
+ end
+
+ def *(h)
+ ConsMethod.new(self, h)
+ end
+
+ def /(h)
+ ApMethod.new([@f, @g, h])
+ end
+
+ def ^(n)
+ if n < 2
+ self
+ else
+ ConsMethod.new(self, self ^ (n-1))
+ end
+ end
+
+
+ def owner
+ @g.owner
+ end
+
+ def receiver
+ @g.receiver
+ end
+
+ def to_proc
+ Proc.new {|x| @f.call(*@g.call(*x)) }
+ end
+
+ def inspect
+ "#<ConsMethod: #{@f.inspect} * #{@g.inspect}>"
+ end
+
+ def arity
+ @g.arity
+ end
+
+ def call(x)
+ @f.call(*@g.call(*x))
+ end
+
+ def [](*x)
+ call(*x)
+ end
+end
20 lib/method_missing/method_extension.rb
@@ -0,0 +1,20 @@
+require 'method_missing/cons_method'
+require 'method_missing/ap_method'
+
+class Method
+ def *(g)
+ ConsMethod.new(self,g)
+ end
+
+ def /(g)
+ ApMethod.new([self, g])
+ end
+
+ def ^(power)
+ if power < 2
+ self
+ else
+ ConsMethod.new(self, self ^ (power-1))
+ end
+ end
+end
3  lib/method_missing/version.rb
@@ -0,0 +1,3 @@
+module MethodMissing
+ VERSION = "0.0.1"
+end
24 method_missing.gemspec
@@ -0,0 +1,24 @@
+# -*- encoding: utf-8 -*-
+$:.push File.expand_path("../lib", __FILE__)
+require "method_missing/version"
+
+Gem::Specification.new do |s|
+ s.name = "method_missing"
+ s.version = MethodMissing::VERSION
+ s.authors = ["Mike Burns"]
+ s.email = ["mike@mike-burns.com"]
+ s.homepage = ""
+ s.license = 'BSD'
+ s.summary = %q{TODO: Write a gem summary}
+ s.description = %q{TODO: Write a gem description}
+
+ s.rubyforge_project = "method_missing"
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
+ s.require_paths = ["lib"]
+
+ s.add_development_dependency('rspec')
+ s.add_development_dependency('rake')
+end
87 spec/method_missing/ap_method_spec.rb
@@ -0,0 +1,87 @@
+require 'spec_helper'
+require 'method_missing/ap_method'
+require 'method_missing/method_extension'
+
+describe ApMethod do
+ def add3_m(x)
+ x + 3
+ end
+ def mul2_m(x)
+ x * 2
+ end
+ def sub1_m(x)
+ x - 1
+ end
+ def div4_m(x)
+ x / 4
+ end
+
+ let(:sub1) { method(:sub1_m) }
+ let(:mul2) { method(:mul2_m) }
+ let(:add3) { method(:add3_m) }
+ let(:div4) { method(:div4_m) }
+
+ subject { ApMethod.new([sub1, mul2, div4]) }
+
+ it "composes" do
+ (subject * add3).call(4).should ==
+ [sub1.call(add3.call(4)),
+ mul2.call(add3.call(4)),
+ div4.call(add3.call(4))]
+ end
+
+ it "sequences" do
+ result = [sub1.call(4), mul2.call(4), div4.call(4), add3.call(4)]
+ (subject / add3).call(4).should == result
+ end
+
+ it "repeats" do
+ power = 3
+ argument = 4
+ result = [
+ (sub1 ^ power).call(argument),
+ (mul2 ^ power).call(argument),
+ (div4 ^ power).call(argument)]
+ (subject ^ power).call(argument).should == result
+ end
+
+ context 'the Method interface' do
+ it "is callable" do
+ subject.call(4).should == [sub1.call(4), mul2.call(4), div4.call(4)]
+ end
+
+ it "has #[] as an alias for #call" do
+ subject[4].should == [sub1.call(4), mul2.call(4), div4.call(4)]
+ end
+
+ it "knows the arity of the method" do
+ subject.arity.should == 1
+ end
+
+ it "inspects into something nice" do
+ expected = "#<ApMethod: #{sub1.inspect} / #{mul2.inspect} / #{div4.inspect}>"
+ subject.inspect.should == expected
+ end
+
+ it "converts itself to a Proc" do
+ subject.to_proc.should be_a(Proc)
+ subject.to_proc.call(4).should == [sub1.call(4), mul2.call(4), div4.call(4)]
+ end
+
+ it "is unable to know the receiver" do
+ expect { subject.receiver }.to raise_error
+ end
+
+ it "is unable to know the method owner" do
+ expect { subject.owner }.to raise_error
+ end
+
+ it "is unable to know its name" do
+ expect { subject.name }.to raise_error
+ end
+
+ it "is unable to unbind" do
+ expect { subject.unbind }.to raise_error
+ end
+ end
+end
75 spec/method_missing/cons_method_spec.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+require 'method_missing/cons_method'
+
+describe ConsMethod do
+ def add3_m(x)
+ x + 3
+ end
+ def mul2_m(x)
+ x * 2
+ end
+ def sub1_m(x)
+ x - 1
+ end
+
+ let(:sub1) { method(:sub1_m) }
+ let(:mul2) { method(:mul2_m) }
+ let(:add3) { method(:add3_m) }
+
+ subject { ConsMethod.new(sub1, mul2) }
+
+ it "composes" do
+ (subject * add3).call(4).should == sub1.call(mul2.call(add3.call(4)))
+ end
+
+ it "sequences" do
+ (subject / add3).call(4).should == [sub1.call(4), mul2.call(4), add3.call(4)]
+ end
+
+ it "repeats" do
+ result = sub1.call(mul2.call(
+ sub1.call(mul2.call(
+ sub1.call(mul2.call(4))))))
+ (subject ^ 3).call(4).should == result
+ end
+
+ context 'the Method interface' do
+ it "is callable" do
+ subject.call(4).should == sub1.call(mul2.call(4))
+ end
+
+ it "has #[] as an alias for #call" do
+ subject[4].should == sub1.call(mul2.call(4))
+ end
+
+ it "knows the arity of the method" do
+ subject.arity.should == 1
+ end
+
+ it "inspects into something nice" do
+ expected = "#<ConsMethod: #{sub1.inspect} * #{mul2.inspect}>"
+ subject.inspect.should == expected
+ end
+
+ it "converts itself to a Proc" do
+ subject.to_proc.should be_a(Proc)
+ subject.to_proc.call(4).should == sub1.call(mul2.call(4))
+ end
+
+ it "knows the receiver" do
+ subject.receiver.inspect.should == mul2.receiver.inspect
+ end
+
+ it "knows the method owner" do
+ subject.owner.should == mul2.owner
+ end
+
+ it "is unable to know its name" do
+ expect { subject.name }.to raise_error
+ end
+
+ it "is unable to unbind" do
+ expect { subject.unbind }.to raise_error
+ end
+ end
+end
26 spec/method_missing_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+require 'method_missing'
+
+describe Method do
+ def sub1_m(x)
+ x - 1
+ end
+ def mul2_m(x)
+ x * 2
+ end
+
+ let(:sub1) { method(:sub1_m) }
+ let(:mul2) { method(:mul2_m) }
+
+ it "adds a composition operator to a method" do
+ (sub1 * mul2).call(3).should == sub1.call(mul2.call(3))
+ end
+
+ it "adds a sequencing operator to a method" do
+ (sub1 / mul2).call(3).should == [sub1.call(3), mul2.call(3)]
+ end
+
+ it "adds a repeat operator to a method" do
+ (mul2 ^ 3).call(3).should == mul2.call(mul2.call(mul2.call(3)))
+ end
+end
1  spec/spec_helper.rb
@@ -0,0 +1 @@
+require 'rspec'
Please sign in to comment.
Something went wrong with that request. Please try again.