method_missing 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *swp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in method_missing.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,30 @@
1
+ Copyright (c)2011, Mike Burns
2
+
3
+ All rights reserved.
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ * Redistributions of source code must retain the above copyright
9
+ notice, this list of conditions and the following disclaimer.
10
+
11
+ * Redistributions in binary form must reproduce the above
12
+ copyright notice, this list of conditions and the following
13
+ disclaimer in the documentation and/or other materials provided
14
+ with the distribution.
15
+
16
+ * Neither the name of Mike Burns nor the names of other
17
+ contributors may be used to endorse or promote products derived
18
+ from this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,89 @@
1
+ method\_missing
2
+ ==============
3
+
4
+ The Method gem you've been waiting for.
5
+
6
+ The method\_missing gem brings the functional tools you need to Ruby: method composition, sequencing, and repeating. These are useful for when you must combine methods, apply different methods to the same argument, or "grow" a method.
7
+
8
+ I'll explain, but first:
9
+
10
+ Installing
11
+ ----------
12
+
13
+ gem install method_missing
14
+
15
+ Usage: Composing
16
+ ----------------
17
+
18
+ This is the classic. In algebra class you learned that `f(g(x))` can also be written `(f . g)(x)`. This is handy because now you have this `(f . g)` object that you can pass to integrals and whatnot.
19
+
20
+ So in Ruby, using the method\_missing gem:
21
+
22
+ def escape_everything(text)
23
+ everything_ecaper.call(text)
24
+ end
25
+
26
+ def everything_ecaper
27
+ method(:html_escape) * method(:escape_javascript) * method(:json_escape)
28
+ end
29
+
30
+ And more algebraically:
31
+
32
+ (f * g).call(x) == f.call(g.call(x))
33
+
34
+ Usage: Sequencing
35
+ -----------------
36
+
37
+ This doesn't come up as often but when it does, oh boy, does it ever! This is useful for when you have a bunch of methods to apply to the same argument. For example, using the method\_missing gem:
38
+
39
+ def valid_options?(options_checker, options)
40
+ options_checker.at_most_one(
41
+ (method(:sort_mod_time) / method(:sort_access_time)).call(options)) &&
42
+ options_checker.at_most_one(
43
+ (method(:sort_file_size) / method(:sort_mod_time)).call(options))
44
+ end
45
+
46
+ Again, more algebraically:
47
+
48
+ (f / g / h).call(x) == [f.call(x), g.call(x), h.call(x)]
49
+
50
+ Usage: Repeating
51
+ ----------------
52
+
53
+ This one comes up the least in most people's day-to-day life. It's the concept of applying a method to its output, repeatedly. Here's a contrived example, using the method\_missing gem:
54
+
55
+ def church_encoding(n)
56
+ if n.zero?
57
+ lambda{|f,x| x}
58
+ else
59
+ lambda{|f,x| (f ^ n).call(x)}
60
+ end
61
+ end
62
+
63
+ Algebraically:
64
+
65
+ (f ^ 3).call(4) == f.call(f.call(f.call(4)))
66
+
67
+ Usage: Combining
68
+ ----------------
69
+
70
+ These are objects which can be combined! Here's an example, also contrived, that turns an object into a number, *n*, then adds four to *n* and multiplies *n* by six, then sums those results, using primitive methods that add one to a number and multiply a number by two:
71
+
72
+ def interesting_math(o)
73
+ four_and_six.call(o).sum
74
+ end
75
+
76
+ def four_and_six
77
+ ((add1 ^ 4) / (mul2 ^ 3)) * method(:to_i)
78
+ end
79
+
80
+ To show this algebraically:
81
+
82
+ (((add1 ^ 4) / (mul2 ^ 3)) * method(:to_i)).call(o) == [
83
+ add1.call(add1.call(add1.call(add1.call(o.to_i)))),
84
+ mul2.call(mul2.call(mul2.call(o.to_i)))
85
+ ]
86
+
87
+ Copyright
88
+ ---------
89
+ Copyright 2011 Mike Burns
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,2 @@
1
+ require "method_missing/version"
2
+ require 'method_missing/method_extension'
@@ -0,0 +1,42 @@
1
+ require 'method_missing/cons_method'
2
+
3
+ class ApMethod
4
+ def initialize(methods)
5
+ @methods = methods
6
+ end
7
+
8
+ def *(f)
9
+ ApMethod.new(@methods.map {|m| ConsMethod.new(m, f)})
10
+ end
11
+
12
+ def /(f)
13
+ ApMethod.new(@methods + [f])
14
+ end
15
+
16
+ def ^(power)
17
+ ApMethod.new(@methods.map {|m| m ^ power})
18
+ end
19
+
20
+
21
+ def call(*x)
22
+ @methods.map{|m| m.call(*x)}
23
+ end
24
+
25
+ def to_proc
26
+ lambda {|*x|
27
+ @methods.map {|m| m.call(*x) }
28
+ }
29
+ end
30
+
31
+ def inspect
32
+ "#<ApMethod: #{@methods.map{|m| m.inspect}.join(' / ')}>"
33
+ end
34
+
35
+ def arity
36
+ @methods.first.arity
37
+ end
38
+
39
+ def [](*x)
40
+ call(*x)
41
+ end
42
+ end
@@ -0,0 +1,53 @@
1
+ require 'method_missing/ap_method'
2
+
3
+ class ConsMethod
4
+ def initialize(f,g)
5
+ @f = f
6
+ @g = g
7
+ end
8
+
9
+ def *(h)
10
+ ConsMethod.new(self, h)
11
+ end
12
+
13
+ def /(h)
14
+ ApMethod.new([@f, @g, h])
15
+ end
16
+
17
+ def ^(n)
18
+ if n < 2
19
+ self
20
+ else
21
+ ConsMethod.new(self, self ^ (n-1))
22
+ end
23
+ end
24
+
25
+
26
+ def owner
27
+ @g.owner
28
+ end
29
+
30
+ def receiver
31
+ @g.receiver
32
+ end
33
+
34
+ def to_proc
35
+ Proc.new {|x| @f.call(*@g.call(*x)) }
36
+ end
37
+
38
+ def inspect
39
+ "#<ConsMethod: #{@f.inspect} * #{@g.inspect}>"
40
+ end
41
+
42
+ def arity
43
+ @g.arity
44
+ end
45
+
46
+ def call(x)
47
+ @f.call(*@g.call(*x))
48
+ end
49
+
50
+ def [](*x)
51
+ call(*x)
52
+ end
53
+ end
@@ -0,0 +1,20 @@
1
+ require 'method_missing/cons_method'
2
+ require 'method_missing/ap_method'
3
+
4
+ class Method
5
+ def *(g)
6
+ ConsMethod.new(self,g)
7
+ end
8
+
9
+ def /(g)
10
+ ApMethod.new([self, g])
11
+ end
12
+
13
+ def ^(power)
14
+ if power < 2
15
+ self
16
+ else
17
+ ConsMethod.new(self, self ^ (power-1))
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ module MethodMissing
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,38 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "method_missing/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "method_missing"
7
+ s.version = MethodMissing::VERSION
8
+ s.authors = ["Mike Burns"]
9
+ s.email = ["mike@mike-burns.com"]
10
+ s.homepage = "http://github.com/mike-burns/method_missing"
11
+ s.license = 'BSD'
12
+ s.summary = %q{Compose, sequence, and repeat Ruby methods.}
13
+ s.description = %q{
14
+ The methods on methods that you've been missing.
15
+
16
+ This gem adds the #* #/ and #^ methods so you can compose, sequence, and
17
+ repeat methods.
18
+
19
+ By composing methods you can express that one method calls another more
20
+ obviously.
21
+
22
+ By sequencing methods you can express that a series of methods have the
23
+ same argument more succinctly.
24
+
25
+ By repeating a method you can compose it with itself as needed, to build
26
+ upon itself.
27
+ }
28
+
29
+ s.rubyforge_project = "method_missing"
30
+
31
+ s.files = `git ls-files`.split("\n")
32
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
33
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
34
+ s.require_paths = ["lib"]
35
+
36
+ s.add_development_dependency('rspec')
37
+ s.add_development_dependency('rake')
38
+ end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+ require 'method_missing/ap_method'
3
+ require 'method_missing/method_extension'
4
+
5
+ describe ApMethod do
6
+ def add3_m(x)
7
+ x + 3
8
+ end
9
+ def mul2_m(x)
10
+ x * 2
11
+ end
12
+ def sub1_m(x)
13
+ x - 1
14
+ end
15
+ def div4_m(x)
16
+ x / 4
17
+ end
18
+
19
+ let(:sub1) { method(:sub1_m) }
20
+ let(:mul2) { method(:mul2_m) }
21
+ let(:add3) { method(:add3_m) }
22
+ let(:div4) { method(:div4_m) }
23
+
24
+ subject { ApMethod.new([sub1, mul2, div4]) }
25
+
26
+ it "composes" do
27
+ (subject * add3).call(4).should ==
28
+ [sub1.call(add3.call(4)),
29
+ mul2.call(add3.call(4)),
30
+ div4.call(add3.call(4))]
31
+ end
32
+
33
+ it "sequences" do
34
+ result = [sub1.call(4), mul2.call(4), div4.call(4), add3.call(4)]
35
+ (subject / add3).call(4).should == result
36
+ end
37
+
38
+ it "repeats" do
39
+ power = 3
40
+ argument = 4
41
+ result = [
42
+ (sub1 ^ power).call(argument),
43
+ (mul2 ^ power).call(argument),
44
+ (div4 ^ power).call(argument)]
45
+ (subject ^ power).call(argument).should == result
46
+ end
47
+
48
+ context 'the Method interface' do
49
+ it "is callable" do
50
+ subject.call(4).should == [sub1.call(4), mul2.call(4), div4.call(4)]
51
+ end
52
+
53
+ it "has #[] as an alias for #call" do
54
+ subject[4].should == [sub1.call(4), mul2.call(4), div4.call(4)]
55
+ end
56
+
57
+ it "knows the arity of the method" do
58
+ subject.arity.should == 1
59
+ end
60
+
61
+ it "inspects into something nice" do
62
+ expected = "#<ApMethod: #{sub1.inspect} / #{mul2.inspect} / #{div4.inspect}>"
63
+ subject.inspect.should == expected
64
+ end
65
+
66
+ it "converts itself to a Proc" do
67
+ subject.to_proc.should be_a(Proc)
68
+ subject.to_proc.call(4).should == [sub1.call(4), mul2.call(4), div4.call(4)]
69
+ end
70
+
71
+ it "is unable to know the receiver" do
72
+ expect { subject.receiver }.to raise_error
73
+ end
74
+
75
+ it "is unable to know the method owner" do
76
+ expect { subject.owner }.to raise_error
77
+ end
78
+
79
+ it "is unable to know its name" do
80
+ expect { subject.name }.to raise_error
81
+ end
82
+
83
+ it "is unable to unbind" do
84
+ expect { subject.unbind }.to raise_error
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+ require 'method_missing/cons_method'
3
+
4
+ describe ConsMethod do
5
+ def add3_m(x)
6
+ x + 3
7
+ end
8
+ def mul2_m(x)
9
+ x * 2
10
+ end
11
+ def sub1_m(x)
12
+ x - 1
13
+ end
14
+
15
+ let(:sub1) { method(:sub1_m) }
16
+ let(:mul2) { method(:mul2_m) }
17
+ let(:add3) { method(:add3_m) }
18
+
19
+ subject { ConsMethod.new(sub1, mul2) }
20
+
21
+ it "composes" do
22
+ (subject * add3).call(4).should == sub1.call(mul2.call(add3.call(4)))
23
+ end
24
+
25
+ it "sequences" do
26
+ (subject / add3).call(4).should == [sub1.call(4), mul2.call(4), add3.call(4)]
27
+ end
28
+
29
+ it "repeats" do
30
+ result = sub1.call(mul2.call(
31
+ sub1.call(mul2.call(
32
+ sub1.call(mul2.call(4))))))
33
+ (subject ^ 3).call(4).should == result
34
+ end
35
+
36
+ context 'the Method interface' do
37
+ it "is callable" do
38
+ subject.call(4).should == sub1.call(mul2.call(4))
39
+ end
40
+
41
+ it "has #[] as an alias for #call" do
42
+ subject[4].should == sub1.call(mul2.call(4))
43
+ end
44
+
45
+ it "knows the arity of the method" do
46
+ subject.arity.should == 1
47
+ end
48
+
49
+ it "inspects into something nice" do
50
+ expected = "#<ConsMethod: #{sub1.inspect} * #{mul2.inspect}>"
51
+ subject.inspect.should == expected
52
+ end
53
+
54
+ it "converts itself to a Proc" do
55
+ subject.to_proc.should be_a(Proc)
56
+ subject.to_proc.call(4).should == sub1.call(mul2.call(4))
57
+ end
58
+
59
+ it "knows the receiver" do
60
+ subject.receiver.inspect.should == mul2.receiver.inspect
61
+ end
62
+
63
+ it "knows the method owner" do
64
+ subject.owner.should == mul2.owner
65
+ end
66
+
67
+ it "is unable to know its name" do
68
+ expect { subject.name }.to raise_error
69
+ end
70
+
71
+ it "is unable to unbind" do
72
+ expect { subject.unbind }.to raise_error
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+ require 'method_missing'
3
+
4
+ describe Method do
5
+ def sub1_m(x)
6
+ x - 1
7
+ end
8
+ def mul2_m(x)
9
+ x * 2
10
+ end
11
+
12
+ let(:sub1) { method(:sub1_m) }
13
+ let(:mul2) { method(:mul2_m) }
14
+
15
+ it "adds a composition operator to a method" do
16
+ (sub1 * mul2).call(3).should == sub1.call(mul2.call(3))
17
+ end
18
+
19
+ it "adds a sequencing operator to a method" do
20
+ (sub1 / mul2).call(3).should == [sub1.call(3), mul2.call(3)]
21
+ end
22
+
23
+ it "adds a repeat operator to a method" do
24
+ (mul2 ^ 3).call(3).should == mul2.call(mul2.call(mul2.call(3)))
25
+ end
26
+ end
@@ -0,0 +1 @@
1
+ require 'rspec'
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: method_missing
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Mike Burns
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-10-26 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :development
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: rake
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ description: "\n The methods on methods that you've been missing.\n\n This gem adds the #* #/ and #^ methods so you can compose, sequence, and\n repeat methods.\n\n By composing methods you can express that one method calls another more\n obviously.\n\n By sequencing methods you can express that a series of methods have the\n same argument more succinctly.\n\n By repeating a method you can compose it with itself as needed, to build\n upon itself.\n "
49
+ email:
50
+ - mike@mike-burns.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files: []
56
+
57
+ files:
58
+ - .gitignore
59
+ - Gemfile
60
+ - LICENSE
61
+ - README.md
62
+ - Rakefile
63
+ - lib/method_missing.rb
64
+ - lib/method_missing/ap_method.rb
65
+ - lib/method_missing/cons_method.rb
66
+ - lib/method_missing/method_extension.rb
67
+ - lib/method_missing/version.rb
68
+ - method_missing.gemspec
69
+ - spec/method_missing/ap_method_spec.rb
70
+ - spec/method_missing/cons_method_spec.rb
71
+ - spec/method_missing_spec.rb
72
+ - spec/spec_helper.rb
73
+ homepage: http://github.com/mike-burns/method_missing
74
+ licenses:
75
+ - BSD
76
+ post_install_message:
77
+ rdoc_options: []
78
+
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ hash: 3
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ hash: 3
96
+ segments:
97
+ - 0
98
+ version: "0"
99
+ requirements: []
100
+
101
+ rubyforge_project: method_missing
102
+ rubygems_version: 1.8.10
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: Compose, sequence, and repeat Ruby methods.
106
+ test_files: []
107
+